<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

<meta name="apple-mobile-web-app-capable" content="yes">
    <link rel="apple-touch-icon" href="https://i.postimg.cc/CMZqNMRh/IMG-6497.jpg">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

    <title>EPhone</title>



    <script src="https://unpkg.com/dexie/dist/dexie.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
    <style>
        @font-face { font-family: 'bulangni'; src: url('') format('truetype'); font-weight: normal; font-style: normal; font-display: swap; }
        :root { --screen-width: 350px; --screen-height: 650px; --secondary-bg: #ffffff; --border-color: #e0e0e0; --text-primary: #1f1f1f; --text-secondary: #8a8a8a; --accent-color: #007bff;   --status-bar-text-color: #000000; }
        html { height: 100%; overflow: hidden; }

/* ▼▼▼ 请用下面这【一整块】全新的代码，替换掉所有旧的 body, phone-frame, phone-screen 等样式 ▼▼▼ */

/* 1. 重置 body，使其成为一个干净的画布 */
body {
    height: 100%;
    overflow: hidden;
    margin: 0;
    font-family: 'bulangni', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-weight: normal;
    /* 背景色可以根据喜好设定，这里用一个柔和的颜色 */
    background-color: #f0f2f5; 
}

/* 2. 让 #phone-screen 成为新的“根”容器，撑满整个浏览器窗口 */
#phone-screen {
    width: 100%;
    height: 100%;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background-color: #fff; 
}

/* 3. 【核心】隐藏掉模拟器的状态栏 */
#status-bar {
    display: none;
}

/* 4. 【核心】让所有页面的头部自动适应iPhone的“刘海”安全区 */
.header, .qzone-header {
    /* 使用 env(safe-area-inset-top) 自动获取顶部安全距离 */
    padding-top: calc(15px + env(safe-area-inset-top));
}

/* 5. 【核心】让聊天输入框和底部导航栏自动适应iPhone底部的“小黑条”安全区 */
#chat-input-area {
    padding-bottom: calc(8px + env(safe-area-inset-bottom));
}

#chat-list-bottom-nav {
     padding-bottom: env(safe-area-inset-bottom);
}

/* ▲▲▲ 替换结束 ▲▲▲ */

        #status-bar { position: absolute; top: 0; left: 0; width: 100%; padding: 12px 20px; display: flex; justify-content: space-between; align-items: center;     color: var(--status-bar-text-color); /* <-- 应用变量 */ z-index: 10; font-size: 14px; box-sizing: border-box; pointer-events: none; }
        #status-bar-time { font-weight: 600; }
        .battery-container { display: flex; align-items: center; gap: 5px; }
        .battery-icon { width: 25px; height: 12px;     border: 1px solid currentColor; border-radius: 3px; position: relative; padding: 1px; }
        .battery-icon::after { content: ''; position: absolute; right: -3px; top: 2px; width: 2px; height: 6px;    background-color: currentColor;  border-radius: 0 1px 1px 0; }
        .battery-level { height: 100%; background-color: currentColor; border-radius: 1px; transition: width 0.5s ease; }
        .battery-container.charging .battery-level { background-color: #4cd964; animation: charge-breath 2s infinite; }
        .battery-container.charging .battery-text { color: #4cd964; }
        @keyframes charge-breath { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
        .screen { width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: flex; flex-direction: column; overflow: hidden; opacity: 0; visibility: hidden; transition: opacity 0.3s, visibility 0.3s; }
        .screen.active { opacity: 1; visibility: visible; z-index: 1; }
        .header { position: relative; z-index: 15; flex-shrink: 0; padding: 15px 20px; padding-top: 45px; background-color: rgba(247, 247, 247, 0.8); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; font-size: 18px; font-weight: 600; }
        .header .header-actions { display: flex; align-items: center; gap: 15px; }
        .header .back-btn, .header .action-btn { font-size: 24px; cursor: pointer; width: 30px; text-align: center; color: var(--accent-color); display: flex; align-items: center; justify-content: center; }

.header .action-btn {
    font-size: 16px; /* 专门为“上传”、“+”等文字按钮缩小字号 */
    font-weight: 600; /* 可以加粗一点让它更清晰 */
}

        .header .action-btn img { height: 26px; }
        .header .save-btn { font-size: 16px; color: var(--accent-color); font-weight: 600; cursor: pointer; }
        #home-screen { display: flex; flex-direction: column; justify-content: flex-start; align-items: center; padding: 20px; padding-top: 80px; padding-bottom: 50px; box-sizing: border-box; background-size: cover; background-position: center; }
        #clock-container { text-align: center; color: white; text-shadow: 0 3px 8px rgba(0,0,0,0.4); margin-bottom: 20px; flex-shrink: 0; }
        #main-time { font-size: 80px; font-weight: 200; }
        #main-date { font-size: 18px; font-weight: 500; }
        .app-grid { margin-top: auto; display: flex; flex-direction: column; align-items: center; gap: 20px; width: 100%; padding: 20px; }
        .app-row { display: flex; justify-content: center; gap: 25px; width: 100%; }
        .app-icon { display: flex; flex-direction: column; align-items: center; cursor: pointer; color: white; text-shadow: 0 1px 3px rgba(0,0,0,0.5); font-size: 14px; font-weight: 500; text-align: center; }
        .app-icon .icon-bg { width: 65px; height: 65px; border-radius: 18px; background-color: var(--secondary-bg); display: flex; justify-content: center; align-items: center; font-size: 32px; margin-bottom: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.15); transition: transform 0.2s ease; overflow: hidden; }
        .app-icon:active .icon-bg { transform: scale(0.9); }
        .app-icon .icon-bg img { width: 100%; height: 100%; object-fit: cover; }
        .app-icon .label { color: white; }
        .form-container, .list-container { padding: 20px; overflow-y: auto; flex-grow: 1; display:flex; flex-direction: column; }
        .form-group { margin-bottom: 20px; }
        .form-group label { display: block; margin-bottom: 8px; font-weight: 500; color: var(--text-secondary); }
        .form-group input, .form-group select, .form-group textarea { width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: 8px; font-size: 16px; box-sizing: border-box; }
        .form-group textarea { min-height: 80px; resize: vertical; }
        #world-book-content-input { height: calc(100% - 120px); }
        .form-button { width: 100%; padding: 15px; background-color: var(--accent-color); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; margin-top: 10px; }
        .form-button-secondary { background-color: #f0f0f0; color: var(--text-primary); border: 1px solid var(--border-color); }
        #wallpaper-screen .form-container { align-items: center; }
        #wallpaper-preview { width: 180px; height: 320px; border: 2px dashed var(--border-color); background-color: #f0f2f5; margin-bottom: 20px; background-size: cover; background-position: center; border-radius: 10px; display: flex; justify-content: center; align-items: center; color: var(--text-secondary); }
        #wallpaper-upload-input { display: none; }
/* 修改后的 #world-book-list 样式 */
#world-book-list {
    flex-grow: 1;
    overflow-y: auto;
    background-color: var(--secondary-bg);
    padding-top: 80px;
    margin-top: -80px;
}

/* 修改后的 #chat-list 样式，去掉了 padding 和 margin */
#chat-list {
    flex-grow: 1;
    background-color: var(--secondary-bg);
    padding-top: 100px; 
    padding-bottom: 50px; /* 为底部导航栏留出空间 */
    box-sizing: border-box;
}

        .list-item { display: flex; flex-direction: column; padding: 12px 20px; cursor: pointer; border-bottom: 1px solid var(--border-color); }
        .list-item:hover { background-color: #f5f5f5; }
        .list-item .item-title { font-weight: 500; font-size: 16px; margin-bottom: 5px; }
        .list-item .item-content { font-size: 14px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .chat-list-item { display: flex; align-items: center; padding: 10px 15px; cursor: pointer; border-bottom: 1px solid var(--border-color); position: relative; }
        .chat-list-item:hover { background-color: #f5f5f5; }
        .chat-list-item .avatar { width: 45px; height: 45px; border-radius: 50%; margin-right: 12px; object-fit: cover; background-color: #ccc; }
        .chat-list-item .info { flex-grow: 1; overflow: hidden; }
        .chat-list-item .name-line { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
        .chat-list-item .name { font-weight: 500; color: var(--text-primary); }
        .chat-list-item .group-tag { font-size: 10px; color: var(--accent-color); background-color: #e7f3ff; padding: 2px 6px; border-radius: 4px; font-weight: bold; flex-shrink: 0; }
        .chat-list-item .last-msg { font-size: 13px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 180px; }
        #chat-interface-screen { background-size: cover; background-position: center; position: relative; }
        #selection-cancel-btn, #selection-delete-btn { font-size: 16px; color: var(--accent-color); cursor: pointer; padding: 5px; }
        #selection-delete-btn { color: #ff3b30; }

/* ▼▼▼ 用这块代码替换掉你原来的 #chat-messages 样式 ▼▼▼ */
#chat-messages {
    flex-grow: 1;
    overflow-y: auto;
    overflow-x: hidden; /* 核心修正1: 强制禁止水平滚动/拖动 */
    padding: 10px 15px; /* 核心修正2: 将左右内边距增加到15px，提供更多呼吸空间 */
    padding-top: 110px;
    margin-top: -80px;
    display: flex;
    flex-direction: column;
    gap: 20px;
    box-sizing: border-box; /* 确保内边距计算正确 */
}
/* ▲▲▲ 替换结束 ▲▲▲ */
        #load-more-btn { text-align: center; padding: 10px; color: var(--accent-color); font-size: 14px; cursor: pointer; background-color: transparent; border: none; width: 100%; }
        #load-more-btn:hover { text-decoration: underline; }

        .sender-name { font-size: 11px; color: #666; margin-bottom: 3px; }

.message-wrapper.ai .sender-name {
    margin-left: 50px; /* 稍微调整，与头像对齐 */
    margin-bottom: 3px;
    position: absolute; /* 让它脱离流，避免影响气泡对齐 */
    top: -16px;       /* 定位到气泡上方 */
    left: 0;
}

/* === 【全新】消息布局与时间戳样式 === */

/* 1. 消息单元的总容器 (重构) */
.message-wrapper {
    display: flex;          /* 使用Flex布局 */
    gap: 8px;               /* 气泡和时间戳之间的间距 */
    align-items: flex-end;  /* 核心：让气泡和时间戳底部对齐 */
    position: relative;
    max-width: 90%;         /* 可以稍微放宽一点，因为时间戳现在在外面了 */
}

/* 2. AI消息单元靠左 */
.message-wrapper.ai {
    align-self: flex-start;
    flex-direction: row; /* 头像、气泡、时间戳，从左到右排列 */
}

/* 3. 用户消息单元靠右 */
.message-wrapper.user {
    align-self: flex-end;
    flex-direction: row-reverse; /* 时间戳、气泡、头像，从右到左排列 */
}

/* 4. 气泡和头像的直接容器 (保持不变) */
.message-bubble {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    max-width: 100%;
}

.timestamp {
    /* 移除旧的 position: absolute */
    font-size: 11px;
    color: #999;
    text-shadow: 0 0 3px rgba(255,255,255,0.6);
    white-space: nowrap; /* 防止时间换行 */
    margin-bottom: 5px;  /* 让它和气泡底部有轻微的对齐偏移，更美观 */
    flex-shrink: 0;      /* 防止被压缩 */
}

        .message-bubble.selected::after { content: '✔'; position: absolute; left: -10px; top: 50%; transform: translateY(-50%); background-color: var(--accent-color); color: white; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; }
        .message-bubble.user.selected::after { left: auto; right: -10px; }

        .message-bubble.user { flex-direction: row-reverse; }
        #typing-indicator { align-self: flex-start; display: none; margin: 0 10px 10px; color: var(--text-secondary); }
        #chat-input-area { flex-shrink: 0; padding: 8px; background-color: rgba(247, 247, 247, 0.8); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-top: 1px solid var(--border-color); display: flex; flex-direction: column; gap: 5px; }
        #chat-input-main-row { display: flex; align-items: flex-end; gap: 8px; width: 100%; }
        #chat-input { flex-grow: 1; border: none; padding: 10px 15px; border-radius: 20px; background-color: var(--secondary-bg); font-size: 16px; max-height: 100px; resize: none; }
        .action-button { border: none; color: white; border-radius: 20px; cursor: pointer; font-weight: 600; font-size: 14px; flex-shrink: 0; }
        #send-btn { background-color: var(--accent-color); height: 40px; padding: 0 15px;}
        .modal { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.4); display: none; justify-content: center; align-items: center; z-index: 100; }
        .modal.visible { display: flex; }
        .modal-content { width: 90%; max-height: 90%; background-color: white; border-radius: 15px; display: flex; flex-direction: column; }
        .modal-header { padding: 15px; font-weight: 600; border-bottom: 1px solid var(--border-color); text-align: center; display: flex; justify-content: space-between; align-items: center; }
        .modal-body { padding: 15px; overflow-y: auto; }
        .modal-footer { padding: 15px; border-top: 1px solid var(--border-color); display: flex; justify-content: space-around; }
        .modal-footer button { width: 45%; padding: 12px; border-radius: 8px; border: 1px solid var(--accent-color); cursor: pointer; font-size: 16px; }
        .modal-footer .save { background-color: var(--accent-color); color: white; }
        .modal-footer .cancel { background-color: white; color: var(--accent-color); }
        .avatar-upload { display: flex; align-items: center; gap: 15px; }
        .avatar-upload img { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; background-color: #eee; }
        .avatar-upload button { padding: 8px 12px; border: 1px solid #ccc; background-color: #f0f0f0; border-radius: 5px; cursor: pointer; }
        #open-persona-library-btn { font-size: 14px; padding: 6px 10px; margin-left: 0; }
        .avatar-upload input[type="file"] { display: none; }
        .theme-selector label { display: inline-flex; align-items: center; margin-right: 15px; margin-bottom: 5px; cursor: pointer; }
        #reset-theme-btn { background: none; border: 1px solid #ccc; color: #555; font-size: 12px; padding: 2px 8px; border-radius: 5px; cursor: pointer; margin-left: 10px; }
        #group-members-settings { display: flex; overflow-x: auto; padding-bottom: 10px; gap: 15px; }
        .member-editor { text-align: center; cursor: pointer; }
        .member-editor img { width: 50px; height: 50px; border-radius: 50%; object-fit: cover; background-color: #eee; margin-bottom: 5px; }
        .member-editor .member-name { font-size: 12px; }
        #notification-bar { position: absolute; top: 40px; left: 50%; width: 90%; z-index: 500; background-color: rgba(250, 250, 250, 0.9); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-radius: 16px; padding: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 12px; cursor: pointer;     transform: translateX(-50%) translateY(-150%); 
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
    visibility: hidden;
}
#notification-bar.visible {
    /* 关键：在Y轴回到原位的同时，保持X轴的居中变换 */
    transform: translateX(-50%) translateY(0);
    visibility: visible;
}
        #notification-avatar { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; }
        #notification-content .name { font-weight: 600; font-size: 15px; color: #000; }
        #notification-content .message { font-size: 14px; color: #555; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
        .sticker-image { max-width: 100px; max-height: 100px; display: block; object-fit: contain; }
        .message-bubble.is-sticker .content, .message-bubble.is-voice-message .content { padding: 0; background-color: transparent; box-shadow: none; border: none; backdrop-filter: none; -webkit-backdrop-filter: none; }
        #chat-input-actions-top { display: flex; gap: 8px; padding: 0 5px; }
        .chat-action-icon-btn { font-size: 24px; padding: 0; width: 38px; height: 38px; line-height: 38px; text-align: center; border-radius: 50%; background-color: rgba(255, 255, 255, 0.5); color: var(--text-primary); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border: 1px solid rgba(0,0,0,0.05); cursor: pointer; display:flex; justify-content:center; align-items:center; }
        #sticker-panel { position: absolute; bottom: 0; left: 0; width: 100%; height: 50%; background-color: rgba(242, 242, 247, 0.85); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); border-top: 1px solid var(--border-color); border-radius: 20px 20px 0 0; z-index: 200; display: flex; flex-direction: column; transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); visibility: hidden; }
        #sticker-panel.visible { transform: translateY(0); visibility: visible; }
        #sticker-panel-header { padding: 10px 20px; display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; border-bottom: 1px solid var(--border-color); }
        #sticker-panel-header .title { font-weight: 600; }
        #sticker-panel-header .panel-btn { font-size: 16px; padding: 5px 10px; cursor: pointer; color: var(--accent-color); }
        #sticker-grid { flex-grow: 1; overflow-y: auto; padding: 15px; display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 15px; }
        .sticker-item { position: relative; aspect-ratio: 1 / 1; background-color: white; border-radius: 10px; background-size: contain; background-repeat: no-repeat; background-position: center; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
        .sticker-item .delete-btn { display: none; position: absolute; top: -5px; right: -5px; width: 20px; height: 20px; background-color: #ff3b30; color: white; border-radius: 50%; text-align: center; line-height: 20px; font-size: 14px; cursor: pointer; border: 2px solid white; }
        #input-actions-wrapper { position: static; display: flex; align-items: flex-end; gap: 8px; flex-shrink: 0; }
        #wait-reply-btn { position: static; bottom: auto; right: auto; width: auto; height: 40px; padding: 0 10px; border-radius: 20px; display: flex; align-items: center; justify-content: center; background-color: rgba(255, 255, 255, 0.6); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border: 1px solid rgba(0,0,0,0.08); box-shadow: 0 2px 5px rgba(0,0,0,0.1); transition: opacity 0.2s, transform 0.1s; cursor: pointer;}
        #wait-reply-btn:hover { opacity: 0.8; }
        #wait-reply-btn:active { transform: scale(0.9); }
        #wait-reply-btn img { height: 22px; display: block; margin: auto; }
        .chat-image { max-width: 100%; border-radius: 10px; display: block; }
        .message-bubble.has-image .content { padding: 5px; }
        #custom-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity 0.2s ease-in-out; }
        #custom-modal-overlay.visible { display: flex; opacity: 1; }
        #custom-modal { background-color: #fff; width: 280px; border-radius: 14px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); display: flex; flex-direction: column; transform: scale(0.95); transition: transform 0.2s ease-in-out; }
        #custom-modal-overlay.visible #custom-modal { transform: scale(1); }
        .custom-modal-header { padding: 16px; font-size: 17px; font-weight: 600; text-align: center; }
        .custom-modal-body { padding: 0 16px 16px; text-align: center; font-size: 14px; color: #333; line-height: 1.5; }
        .custom-modal-body p { margin: 0; margin-bottom: 12px; }
        .custom-modal-body input { width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #ccc; font-size: 14px; box-sizing: border-box; }
        .custom-modal-footer { border-top: 1px solid #dbdbdb; display: flex; }
        .custom-modal-footer button { flex: 1; background: none; border: none; padding: 12px; font-size: 17px; cursor: pointer; color: var(--accent-color); }
        .custom-modal-footer button:first-child { border-right: 1px solid #dbdbdb; }
        .custom-modal-footer .confirm-btn { font-weight: 600; }
        .custom-modal-footer .confirm-btn.btn-danger { color: #ff3b30; }
        #preset-actions-modal .custom-modal-footer { flex-direction: column; }
        #preset-actions-modal .custom-modal-footer button { width: 100%; border: none; border-bottom: 1px solid #dbdbdb; padding: 14px; font-size: 18px; }
        #preset-actions-modal .custom-modal-footer button:last-child { border-bottom: none; }
        .custom-multiselect { position: relative; user-select: none; }
        .select-box { display: flex; align-items: center; width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: 8px; font-size: 16px; box-sizing: border-box; background-color: #fff; cursor: pointer; }
        .select-box .selected-options-text { flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-primary); }
        .select-box .arrow-down { margin-left: auto; font-size: 10px; color: var(--text-secondary); transition: transform 0.2s; }
        .select-box.expanded .arrow-down { transform: rotate(180deg); }

.checkboxes-container {
    display: none;
    position: absolute;
    /* 核心修改：不再使用 top，而是用 margin-top 来创造间距，更稳定 */
    top: 100%; 
    margin-top: 5px; /* <-- 新增：向下推开5像素的距离 */
    left: 0;
    right: 0;
    max-height: 150px;
    overflow-y: auto;
    overflow-x: hidden;
    background-color: #fff;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    z-index: 101;
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

        .checkboxes-container.visible { display: block; }
        .checkboxes-container label { display: block; padding: 10px 12px; cursor: pointer; font-weight: normal; color: var(--text-primary); }

.checkboxes-container label {
    display: block;
    padding: 12px 15px; /* <-- 修改：增加了上下和左右的内边距，让每一行更高更宽 */
    cursor: pointer;
    font-weight: normal;
    color: var(--text-primary);
    font-size: 15px; /* <-- 新增：将字体大小从默认值放大到15px */
}

        .checkboxes-container input { margin-right: 10px; vertical-align: middle; }
        .bg-upload-container { display: flex; align-items: center; gap: 10px; margin-top: 8px; flex-wrap: wrap; }
        .bg-preview-img { max-width: 120px; max-height: 80px; border-radius: 8px; border: 1px solid var(--border-color); object-fit: cover; display: none; }
        #remove-bg-btn { padding: 8px 12px; border: 1px solid #ff3b30; color: #ff3b30; background-color: #fff; border-radius: 5px; cursor: pointer; font-size: 14px; display: none; }
        .message-bubble.is-ai-image .content { padding: 5px; background: transparent; box-shadow: none; border: none; backdrop-filter: none; -webkit-backdrop-filter: none; }
        .ai-generated-image { max-width: 180px; border-radius: 12px; display: block; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; }
        .ai-generated-image:hover { transform: scale(1.05); box-shadow: 0 4px 15px rgba(0,0,0,0.2); }
        .voice-message-body { display: flex; align-items: center; cursor: pointer; padding: 8px 12px; min-width: 80px; max-width: 200px; }
        .message-bubble.user .voice-message-body { color: #1a3d00; flex-direction: row-reverse; }
        .message-bubble.ai .voice-message-body { color: var(--text-primary); }
        .voice-waveform { display: flex; align-items: center; height: 20px; gap: 2px; flex-grow: 1; margin: 0 10px; }
        .voice-waveform div { width: 3px; background-color: currentColor; border-radius: 2px; animation: wave-quiet 1.5s ease-in-out infinite; }
        @keyframes wave-quiet { 0%, 100% { height: 2px; } 50% { height: 10px; } }
        .voice-waveform div:nth-child(2) { animation-delay: 0.2s; } .voice-waveform div:nth-child(3) { animation-delay: 0.4s; } .voice-waveform div:nth-child(4) { animation-delay: 0.6s; } .voice-waveform div:nth-child(5) { animation-delay: 0.8s; }
.voice-duration {
    /* --- 核心修正 --- */
    font-size: var(--chat-font-size, 13px);
    /* --- 修正结束 --- */
    font-weight: 500;
    color: var(--text-secondary);
}
        .message-bubble.user .voice-duration { color: #3e6224; }

/* ▼▼▼ 用这块代码替换掉你原来的 .message-bubble .content 样式 ▼▼▼ */
/* 通用内容区样式，为时间戳和字体大小做准备 */
.message-bubble .content {
    position: relative;
    font-size: var(--chat-font-size, 16px);
    padding: 8px 12px;
    line-height: 1.5;
    word-break: break-word; /* 核心修正: 强制长单词或URL换行，防止撑破气泡 */
}
/* ▲▲▲ 替换结束 ▲▲▲ */

        /* === 气泡主题样式 === */
        .message-bubble.user .content { background-color: rgba(255, 255, 255, 0.75); color: #585858; border-radius: 8px 2px 8px 8px; }
        .message-bubble.ai .content { background-color: rgba(255, 255, 255, 0.7); color: #585858; border-radius: 2px 8px 8px 8px; }
      
.message-bubble::after {
    content: "";
    position: absolute;
    width: 20px;  
    height: 20px; 
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 1; 
    z-index: 1;
}
      
        #chat-messages[data-theme="pink_blue"] .message-bubble.user .content { background-color: #fff0f6; color: #432531; }
        #chat-messages[data-theme="pink_blue"] .message-bubble.ai .content { background-color: #eff7ff; color: #263a4e; }
        #chat-messages[data-theme="blue_white"] .message-bubble.user .content { background-color: #eff7ff; color: #263a4e; }
        #chat-messages[data-theme="blue_white"] .message-bubble.ai .content { background-color: #f8f9fa; color: #383d41; }
        #chat-messages[data-theme="purple_yellow"] .message-bubble.user .content { background-color: #faf7ff; color: #827693; }
        #chat-messages[data-theme="purple_yellow"] .message-bubble.ai .content { background-color: #fffde4; color: #5C4033; }
        #chat-messages[data-theme="black_white"] .message-bubble.user .content { background-color: #343a40; color: #f8f9fa; }
        #chat-messages[data-theme="black_white"] .message-bubble.ai .content { background-color: #f8f9fa; color: #343a40; }
        #chat-messages[data-theme="yellow_white"] .message-bubble.user .content { background-color: #FFEB3B; color: #5D4037; }
        #chat-messages[data-theme="yellow_white"] .message-bubble.ai .content { background-color: #f8f9fa; color: #383d41; }
        #chat-messages[data-theme="red_black"] .message-bubble.user .content { background-color: #C62828; color: #FFFFFF; }
        #chat-messages[data-theme="red_black"] .message-bubble.ai .content { background-color: #212121; color: #FFFFFF; }
        #chat-messages[data-theme="blue_yellow"] .message-bubble.user .content { background-color: #A0D2EB; color: #153243; }
        #chat-messages[data-theme="blue_yellow"] .message-bubble.ai .content { background-color: #FEF9E7; color: #5D4037; }
        #chat-messages[data-theme="pink_yellow"] .message-bubble.user .content { background-color: #fff0f6; color: #432531; }
        #chat-messages[data-theme="pink_yellow"] .message-bubble.ai .content { background-color: #FEF9E7; color: #5D4037; }
        #chat-messages[data-theme="pink_purple"] .message-bubble.user .content { background-color: #fff0f6; color: #a78396; }
        #chat-messages[data-theme="pink_purple"] .message-bubble.ai .content { background-color: #faf7ff; color: #827693; }
        #chat-messages[data-theme="gray_white"] .message-bubble.user .content { background-color: #e9ecef; color: #495057; }
        #chat-messages[data-theme="gray_white"] .message-bubble.ai .content { background-color: #f8f9fa; color: #383d41; }
        #chat-messages[data-theme="blue_green"] .message-bubble.user .content { background-color: #d1ecf1; color: #0c5460; }
        #chat-messages[data-theme="blue_green"] .message-bubble.ai .content { background-color: #d4edda; color: #155724; }
        #chat-messages[data-theme="pink_white"] .message-bubble.user .content { background-color: #fff0f6; color: #a78396; }
        #chat-messages[data-theme="pink_white"] .message-bubble.ai .content { background-color: #f8f9fa; color: #383d41; }
        #chat-messages[data-theme="pink_black"] .message-bubble.user .content { background-color: #F8BBD0; color: #5B2C6F; }
        #chat-messages[data-theme="pink_black"] .message-bubble.ai .content { background-color: #343a40; color: #f8f9fa; }
        #chat-messages[data-theme="pink_green"] .message-bubble.user .content { background-color: #F8BBD0; color: #5B2C6F; }
        #chat-messages[data-theme="pink_green"] .message-bubble.ai .content { background-color: #C8E6C9; color: #1B5E20; }
        #chat-messages[data-theme="green_black"] .message-bubble.user .content { background-color: #d4edda; color: #155724; }
        #chat-messages[data-theme="green_black"] .message-bubble.ai .content { background-color: #343a40; color: #f8f9fa; }

        #transfer-btn { font-weight: bold; }
        #transfer-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); display: none; align-items: center; justify-content: center; z-index: 1001; }
        #transfer-modal.visible { display: flex; }
        .transfer-content { background-color: #fff0f5; border-radius: 20px; width: 290px; padding: 20px; box-shadow: 0 5px 25px rgba(255, 105, 180, 0.3); text-align: center; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" opacity="0.05"><path d="M50,4 C35,4 28,15 28,24 C28,33 35,32 35,40 C35,48 28,49 28,57 C28,65 35,66 35,74 C35,82 28,83 28,91 C28,99 35,100 50,100 C65,100 72,99 72,91 C72,83 65,82 65,74 C65,66 72,65 72,57 C72,49 65,48 65,40 C65,32 72,33 72,24 C72,15 65,4 50,4 Z" fill="%23FF69B4"/></svg>'); background-repeat: no-repeat; background-position: top right; background-size: 80px; }
        .transfer-header { font-size: 20px; font-weight: bold; color: #a35c7b; margin-bottom: 20px; }
        .transfer-input-group { margin-bottom: 15px; text-align: left; }
        .transfer-input-group label { display: block; font-size: 14px; color: #ff85b3; margin-bottom: 5px; font-weight: 500; }
        .transfer-input-group input { width: 100%; padding: 12px; border-radius: 10px; border: 2px solid #ffcce0; background-color: #fff; font-size: 16px; box-sizing: border-box; }
        .transfer-input-group input:focus { border-color: #ff85b3; outline: none; }
        .transfer-actions { display: flex; justify-content: space-between; gap: 10px; }
        .transfer-actions button { flex: 1; padding: 12px; border: none; border-radius: 10px; font-size: 16px; font-weight: bold; cursor: pointer; transition: transform 0.2s; }
        .transfer-actions button:active { transform: scale(0.95); }
        #transfer-cancel-btn { background-color: #ffdde9; color: #a35c7b; }
        #transfer-confirm-btn { background-color: #ff85b3; color: white; }
        .message-bubble.is-transfer .content { padding: 0; background: transparent; box-shadow: none; border: none; backdrop-filter: none; -webkit-backdrop-filter: none; cursor: pointer; }
        .transfer-card { width: 200px; border-radius: 12px; padding: 12px; color: white; position: relative; overflow: hidden; }
        .transfer-card::before { content: '🐾'; position: absolute; right: 10px; top: 5px; font-size: 30px; opacity: 0.2; transform: rotate(15deg); }
        .message-bubble.user .transfer-card { background: radial-gradient(circle at top left, #ffc5d5, #ff85b3); }
        .message-bubble.ai .transfer-card { background: radial-gradient(circle at top left, #a1c4fd, #c2e9fb); }
        .transfer-title { font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
        .transfer-amount { font-size: 28px; font-weight: bold; margin-bottom: 4px; }
        .transfer-note { font-size: 13px; opacity: 0.9; border-top: 1px solid rgba(255,255,255,0.3); padding-top: 8px; margin-top: 8px; word-break: break-all; }
        
        @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        #listen-together-btn img.rotating { animation: spin 2s linear infinite; }
        #listen-together-btn img.paused { animation-play-state: paused; }
        #music-player-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 50; display: none; justify-content: center; align-items: center; background-color: rgba(0,0,0,0.3); }
        #music-player-overlay.visible { display: flex; }
        .music-player-window { width: 90%; background-color: rgba(255, 255, 255, 0.6); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-radius: 20px; box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); border: 1px solid rgba(255, 255, 255, 0.18); padding: 25px; display: flex; flex-direction: column; align-items: center; color: #1f1f1f; position: relative; }
        #music-playlist-btn { position: absolute; top: 15px; right: 15px; font-size: 24px; cursor: pointer; color: #333; }
        #music-time-counter { font-size: 12px; color: #555; margin-bottom: 20px; }
        #music-player-song-title { font-size: 20px; font-weight: 600; margin-bottom: 5px; text-align: center; }
        #music-player-artist { font-size: 14px; color: #666; margin-bottom: 25px; }
        .music-controls { display: flex; align-items: center; justify-content: center; gap: 20px; width: 100%; margin-bottom: 30px; }
        .music-controls button { background: none; border: none; font-size: 16px; font-weight: bold; cursor: pointer; color: #333; width: 44px; height: 44px; display: flex; justify-content: center; align-items: center; transition: transform 0.2s; }
        .music-controls button:active { transform: scale(0.9); }
        .music-controls .play-pause-btn { font-size: 24px; width: 60px; height: 60px; border-radius: 50%; background-color: rgba(0,0,0,0.05); }
        .music-bottom-actions { display: flex; justify-content: space-between; width: 100%; }
        .music-bottom-actions button { flex: 1; padding: 12px 0; border: none; border-radius: 10px; font-size: 15px; font-weight: 500; cursor: pointer; }
        #music-exit-btn { background-color: rgba(255, 100, 100, 0.7); color: white; margin-right: 5px; }
        #music-return-btn { background-color: rgba(0, 123, 255, 0.7); color: white; margin-left: 5px; }
        
        #music-playlist-panel { position: absolute; bottom: 0; left: 0; width: 100%; height: 70%; background-color: rgba(242, 242, 247, 0.9); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); border-top: 1px solid var(--border-color); border-radius: 20px 20px 0 0; z-index: 210; display: flex; flex-direction: column; transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); visibility: hidden; }
        #music-playlist-panel.visible { transform: translateY(0); visibility: visible; }
        .playlist-header { padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; border-bottom: 1px solid var(--border-color); font-weight: 600; }
        .playlist-header .panel-btn { font-size: 16px; cursor: pointer; color: var(--accent-color); }
        .playlist-body { flex-grow: 1; overflow-y: auto; padding: 10px 0; }
        .playlist-item { padding: 10px 20px; display: flex; justify-content: space-between; align-items: center; cursor: pointer; border-bottom: 1px solid #eee; }
        .playlist-item.playing { background-color: rgba(0, 123, 255, 0.1); }
        .playlist-item-info .title { font-weight: 500; font-size: 15px; }
        .playlist-item-info .artist { font-size: 12px; color: #666; }
        .playlist-item .delete-track-btn { color: #ff3b30; font-size: 20px; padding: 5px; }

        /* Persona Library Styles */
        #persona-library-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; padding: 10px; }
        .persona-preset-item { aspect-ratio: 1 / 1; border-radius: 12px; background-size: cover; background-position: center; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; border: 1px solid rgba(0,0,0,0.1); }
        .persona-preset-item:hover { transform: scale(1.08); box-shadow: 0 4px 10px rgba(0,0,0,0.2); }
        .modal-header .action-button { font-size: 16px; color: var(--accent-color); font-weight: 600; cursor: pointer; background: none; border: none; padding: 5px; }
        
        /* Battery Alert Modal Styles */
        #battery-alert-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.4); display: none; justify-content: center; align-items: center; z-index: 2000; opacity: 0; transition: opacity 0.3s ease; }
        #battery-alert-modal.visible { display: flex; opacity: 1; }
        .battery-alert-content { background-color: rgba(255, 255, 255, 0.9); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); width: 280px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.2); text-align: center; padding: 20px; cursor: pointer; transform: scale(0.9); transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
        #battery-alert-modal.visible .battery-alert-content { transform: scale(1); }
        #battery-alert-image { max-width: 100px; max-height: 100px; margin-bottom: 15px; }
        #battery-alert-text { font-size: 16px; font-weight: 500; color: #333; margin: 0; line-height: 1.4; }

/* 这是你要添加的新样式 */
#font-preview {
    transition: font-family 0.3s ease;}

/* === 聊天列表界面新增样式 (这是新添加的) === */
#chat-list-screen {
}

.chat-list-view {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s, visibility 0.2s;
    z-index: 1; 
}
.chat-list-view.active {
    opacity: 1;
    visibility: visible;
    z-index: 2; 
}

#messages-view {
    overflow-y: auto; 
}

/* 底部导航栏样式 */
#chat-list-bottom-nav {
    position: absolute; /* 让它固定在底部 */
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 15; /* 确保它在视图之上 */
    display: flex;
    border-top: 1px solid var(--border-color);
    background-color: rgba(247, 247, 247, 0.9);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

.nav-item {
    flex: 1;
    text-align: center;
    padding: 12px 0;
    font-size: 14px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.2s;
}

.nav-item.active {
    color: var(--accent-color);
    font-weight: 600;
}

/* === 动态界面 (QZone) 样式 (这是新添加的) === */
#qzone-screen {
    background-color: #f0f2f5;
}

.qzone-header {
    /* position: absolute;  <-- 把这个改成 relative */
    position: relative;
    z-index: 10; /* z-index 保持，或者可以更高 */
    flex-shrink: 0; /* 防止被压缩 */
    padding: 15px 20px;
    padding-top: 45px;
    background-color: rgba(247, 247, 247, 0.7); 
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 18px;
    font-weight: 600;
    text-align: center;
}

.qzone-header .back-btn {
    font-size: 24px;
    cursor: pointer;
    color: var(--accent-color);
}

.qzone-header span:nth-child(2) { /* "好友动态"文字 */
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
}

.qzone-content {
    flex-grow: 1;
    overflow-y: auto;
    /* padding-top: 80px;  <-- 删除这个，因为header不再是absolute了 */
}

.qzone-profile-header {
    position: relative;
    margin-bottom: 20px;
}

.qzone-banner-container {
    width: 100%;
    height: 180px; /* 背景板高度 */
    position: relative;
}

#qzone-banner-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.qzone-user-info {
    position: absolute;
    bottom: -30px; /* 让头像和昵称区域向下偏移，一半在背景板内，一半在外 */
    left: 20px;
    display: flex;
    align-items: flex-end; /* 让昵称和头像底部对齐 */
    gap: 10px;
}

.qzone-avatar-container {
    position: relative;
}

#qzone-avatar-img {
    width: 70px;
    height: 70px;
    border-radius: 50%;
    border: 3px solid white;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    object-fit: cover;
}

#qzone-nickname {
    font-size: 18px;
    font-weight: 600;
    color: #fff;
    text-shadow: 0 1px 3px rgba(0,0,0,0.5);
    padding-bottom: 5px; /* 微调位置 */
}

/* 编辑按钮的通用样式 */
.qzone-edit-btn {
    position: absolute;
    background-color: rgba(0,0,0,0.4);
    color: white;
    border: none;
    border-radius: 10px;
    padding: 4px 8px;
    font-size: 12px;
    cursor: pointer;
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
}

#change-qzone-banner-btn {
    bottom: 10px;
    right: 10px;
}

#change-qzone-avatar-btn {
    bottom: 5px;
    right: 5px;
}

#change-qzone-nickname-btn {
    font-size: 14px;
    padding: 2px 6px;
    margin-left: 5px; /* 与昵称的间距 */
    color: var(--text-primary);
    background-color: rgba(255,255,255,0.7);
    border-radius: 5px;
    position: relative; /* 脱离flex布局的对齐 */
    bottom: 5px; /* 微调垂直位置 */
}

/* === 让编辑功能更“隐形” === */
#qzone-banner-container,
#qzone-avatar-container,
#qzone-nickname {
    cursor: pointer; /* 鼠标悬停时显示为可点击手势 */
    transition: opacity 0.2s;
}
#qzone-banner-container:hover,
#qzone-avatar-container:hover,
#qzone-nickname:hover {
    opacity: 0.85; /* 悬停时稍微变暗，给用户反馈 */
}
/* 隐藏掉旧的、独立的编辑按钮 */
.qzone-edit-btn {
    display: none;
}

/* === 控制 Header 和 Bottom Nav 的显隐 === */
/* 默认隐藏动态界面的 Header */
#qzone-screen .qzone-header {
    display: none;
}
/* 当动态视图激活时，显示它的Header */
#qzone-screen.active .qzone-header {
    display: flex;
}

/* 当进入动态视图时，隐藏主Header和底部导航栏 */
#chat-list-screen.in-qzone-view > .header,
#chat-list-screen.in-qzone-view > #chat-list-bottom-nav {
    display: none;
}

.chat-group-container:first-child {
    margin-top: 10px; 
}

/* ▲▲▲ 新样式替换结束 ▲▲▲ */

/* ▼▼▼ 把所有这些新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 动态功能栏样式 === */
.qzone-actions-bar {
    display: flex;
    justify-content: space-around;
    padding: 10px 0;
    margin: 40px 15px 15px 15px; /* 上边距更大，为浮动的头像留出空间 */
    background-color: var(--secondary-bg);
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}

.action-item {
    flex: 1;
    text-align: center;
    font-size: 15px;
    font-weight: 500;
    color: var(--text-primary);
    cursor: pointer;
    padding: 8px 0;
    position: relative;
}

/* 用伪元素创建分隔线 */
.action-item:not(:last-child)::after {
    content: '';
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
    height: 20px;
    background-color: var(--border-color);
}

/* === 动态帖子列表样式 === */
#qzone-posts-list {
    padding: 0 15px 20px 15px; /* 左右和底部留出边距 */
    display: flex;
    flex-direction: column;
    gap: 20px; /* 帖子之间的间距 */
}

.qzone-post-item {
    background-color: var(--secondary-bg);
    border-radius: 12px;
    padding: 15px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}

.post-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.post-header .post-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
}

.post-info {
    display: flex;
    flex-direction: column;
}

.post-info .post-nickname {
    font-weight: 600;
    font-size: 15px;
    color: var(--text-primary);
}

.post-info .post-timestamp {
    font-size: 12px;
    color: var(--text-secondary);
}

.post-content {
    font-size: 14px;
    line-height: 1.6;
    color: #333;
    white-space: pre-wrap; /* 让换行符生效 */
    word-break: break-word; /* 防止长单词溢出 */
}

/* ▲▲▲ 新样式结束 ▲▲▲ */

/* ▼▼▼ 新样式粘贴到末尾 ▼▼▼ */

/* === 发布动态模态框样式 === */
#post-public-text {
    min-height: 80px; /* 确保文本域有足够的高度 */
    resize: vertical;
}

.post-image-preview-container {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9; /* 保持16:9的预览比例 */
    background-color: #f0f2f5;
    border: 2px dashed var(--border-color);
    border-radius: 8px;
    margin-bottom: 15px;
    display: none; /* 默认隐藏 */
    justify-content: center;
    align-items: center;
}
.post-image-preview-container.visible {
    display: flex; /* 上传后显示 */
}

#post-image-preview {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: 6px;
}

#post-remove-image-btn {
    position: absolute;
    top: -10px;
    right: -10px;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background-color: #ff3b30;
    color: white;
    border: 2px solid white;
    font-size: 16px;
    line-height: 20px;
    text-align: center;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0,0,0,0.3);
}

.post-image-upload-options {
    display: flex;
    gap: 10px;
}

.post-image-upload-options button {
    flex: 1;
    margin-top: 0;
}

/* ▲▲▲ 新样式结束 ▲▲▲ */

/* ▼▼▼ 新样式 ▼▼▼ */

/* === 发布动态模态框 - 模式切换样式 === */
.post-mode-switcher {
    display: flex;
    margin-bottom: 20px;
    background-color: #e9ecef;
    border-radius: 8px;
    padding: 4px;
}

.mode-btn {
    flex: 1;
    padding: 8px;
    border: none;
    background-color: transparent;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.2s ease-in-out;
}

.mode-btn.active {
    background-color: var(--secondary-bg);
    color: var(--text-primary);
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.post-mode-content {
    display: none; /* 默认都隐藏 */
}

.post-mode-content.active {
    display: block; /* 激活的才显示 */
}

/* ▲▲▲ 新样式结束 ▲▲▲ */

/* === 相册页面背景色 === */
#album-screen {
    background-color: #f0f2f5; /* 使用一个柔和的浅灰色，比纯白更护眼 */
}

/* === 相册页面网格布局 === */
#album-grid-page {
    padding: 15px;
    display: grid;
    grid-template-columns: repeat(2, 1fr); /* 每行显示2个相册 */
    gap: 15px;
}

/* === 相册项目样式 (美化) === */
.album-item {
    display: flex;
    flex-direction: column;
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    border-radius: 8px; /* 给整个项目也加个圆角 */
}

.album-item:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 15px rgba(0,0,0,0.1);
}

.album-cover {
    aspect-ratio: 1 / 1; /* 保持封面为正方形 */
    background-size: cover;
    background-position: center;
    border-radius: 8px;
    margin-bottom: 8px;
    background-color: #f0f2f5; /* 封面加载前的占位颜色 */
}

.album-info {
    text-align: center;
}

.album-name {
    font-weight: 500;
    margin: 0 0 4px 0;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis; /* 防止长名字换行 */
}

.album-count {
    font-size: 12px;
    color: var(--text-secondary);
    margin: 0;
}

/* ▲▲▲ 新的 CSS 粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这些新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 相册照片详情页 === */
#album-photos-screen {
    background-color: #f0f2f5;
}

#photos-grid-page {
    padding: 15px;
    display: grid;
    /* 每行显示3张照片，并保持间距 */
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.photo-item {
    position: relative; /* 为了定位删除按钮 */
    aspect-ratio: 1 / 1; /* 保持照片为正方形 */
    border-radius: 6px;
    overflow: hidden; /* 防止图片溢出圆角 */
    background-color: #e9ecef; /* 图片加载前的占位符颜色 */
}

.photo-item .photo-thumb {
    width: 100%;
    height: 100%;
    object-fit: cover; /* 保证图片填满容器且不变形 */
    cursor: pointer;
}

/* 删除按钮的样式 */
.photo-item .photo-delete-btn {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 22px;
    height: 22px;
    background-color: rgba(0, 0, 0, 0.6);
    color: white;
    border: none;
    border-radius: 50%;
    font-size: 16px;
    line-height: 22px;
    text-align: center;
    cursor: pointer;
    opacity: 0; /* 默认隐藏 */
    transition: opacity 0.2s ease;
}

/* 鼠标悬停在照片上时显示删除按钮 */
.photo-item:hover .photo-delete-btn {
    opacity: 1;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* === 图片查看器模态框样式 === */
#photo-viewer-modal {
    background-color: rgba(0, 0, 0, 0.85);
    z-index: 1002;
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
}

.photo-viewer-content {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
}

#photo-viewer-image {
    max-width: 90vw;  /* 图片最大宽度为视口的90% */
    max-height: 85vh; /* 图片最大高度为视口的85% */
    object-fit: contain;
    border-radius: 8px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
    /* 为图片的切换添加一点平滑的淡入淡出效果 */
    transition: opacity 0.2s ease-in-out;
}

/* 关闭按钮 */
#photo-viewer-close-btn {
    position: absolute;
    top: 20px;
    right: 20px;
    background: none;
    border: none;
    color: white;
    font-size: 40px;
    font-weight: 200;
    cursor: pointer;
    line-height: 1;
    text-shadow: 0 0 5px black;
}

/* 左右导航箭头 */
#photo-viewer-modal .nav-arrow {
    position: absolute; /* 现在我们用绝对定位来控制箭头 */
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.7);
    font-size: 50px; /* 在手机屏幕上，可以稍微小一点 */
    font-weight: 100;
    cursor: pointer;
    padding: 10px; /* 调整内边距 */
    user-select: none;
    transition: color 0.2s;
    z-index: 1003; /* 确保箭头在最上层 */
}

#photo-viewer-prev-btn {
    left: 5px; /* 定位左箭头 */
}

#photo-viewer-next-btn {
    right: 5px; /* 定位右箭头 */
}

#photo-viewer-modal .nav-arrow:hover {
    color: white;
}

/* 当箭头被禁用时（比如第一张或最后一张） */
#photo-viewer-modal .nav-arrow:disabled {
    color: rgba(255, 255, 255, 0.2);
    cursor: default;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这些新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* ▼▼▼ 请用这块新CSS替换掉上一版的交互区CSS ▼▼▼ */

/* === 帖子内容区 - 相对定位容器 === */
/* === 帖子内容区 === */
.post-main-content {
    /* 它现在只是一个普通的内容容器，不再需要特殊样式了 */
}

/* === 帖子互动图标区 (新样式) === */
.post-feedback-icons {
    display: flex;
    justify-content: flex-end; /* 让图标靠右对齐 */
    align-items: center;
    gap: 12px;
    padding: 8px 0; /* 核心修改：给图标区域上下各8px的留白 */
}

.action-icon {
    cursor: pointer;
    color: var(--text-secondary); /* 默认灰色 */
    transition: all 0.2s ease-in-out;
}

.action-icon svg {
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* 图标激活(点赞/收藏后)的样式 */
.action-icon.active {
    color: #ff5252; /* 激活后变红色 */
    transform: scale(1.1); /* 轻微放大 */
}

.action-icon.active.favorite {
    color: #ffc107; /* 收藏用黄色 */
}

.action-icon.active svg {
    fill: currentColor; /* 激活后填充颜色 */
}

/* 点击时的动画效果 */
.animate-like {
    animation: like-bounce 0.4s ease-in-out;
}

@keyframes like-bounce {
    0%   { transform: scale(1); }
    25%  { transform: scale(0.8); }
    50%  { transform: scale(1.2); }
    75%  { transform: scale(1.05); }
    100% { transform: scale(1.1); }
}


/* === 帖子底部评论区样式 (现在是独立部分) === */
.post-footer {
    margin-top: 15px;
    padding-top: 10px;
    border-top: 1px solid #f0f0f0; /* 用一条浅色线分隔 */
    display: flex;
    align-items: center;
    gap: 8px; /* 调整整体间距 */
}

/* 评论区容器 */
.comment-section {
    flex-grow: 1; /* 占据大部分空间 */
    display: flex;
    align-items: center;
    gap: 8px;
}

.comment-section .comment-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.comment-section .comment-input {
    width: 100%;
    padding: 8px 12px;
    border: none;
    background-color: #f0f2f5;
    border-radius: 14px;
    font-size: 13px;
    outline: none;
}

/* 新增的发送按钮样式 */
.comment-send-btn {
    flex-shrink: 0; /* 防止被压缩 */
    padding: 8px 15px;
    border: none;
    background-color: var(--accent-color);
    color: white;
    border-radius: 14px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 未读消息小红点通用样式 === */
.unread-indicator {
    position: absolute;
    top: -8px;      
    right: -15px;    
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    background-color: #ff3b30;
    color: white;
    font-size: 11px;
    font-weight: bold;
    line-height: 18px;
    text-align: center;
    border-radius: 9px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.2);
    display: none;
    z-index: 1;
}

/* 聊天界面返回按钮上的小红点 (只显示点，不显示数字) */
.back-btn-indicator {
    top: 0;
    right: -8px; /* 放到返回箭头右上角 */
    width: 10px;
    height: 10px;
    min-width: 10px;
    padding: 0;
    border-radius: 50%;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 评论列表容器 === */
.post-comments-container {
    padding: 10px 0; /* 上下留白 */
    display: flex;
    flex-direction: column;
    gap: 8px; /* 评论之间的间距 */
    font-size: 13px; /* 统一评论区字体大小 */
}

/* 每一条评论 */
.comment-item {
    line-height: 1.5;
}

/* 评论者的名字，加粗并使用主题色 */
.comment-item .commenter-name {
    font-weight: 600;
    color: var(--accent-color);
    cursor: pointer;
    margin-right: 5px; /* 和评论内容之间留点空隙 */
}

/* 评论内容 */
.comment-item .comment-text {
    color: var(--text-primary);
    word-break: break-word;
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 帖子点赞区域样式 === */
.post-likes-section {
    display: flex;
    align-items: center;
    gap: 6px; /* 图标和文字的间距 */
    padding: 8px 10px; /* 内边距 */
    font-size: 13px;
    color: var(--accent-color); /* 使用主题蓝色 */
    background-color: #f0f5fa; /* 给一个淡淡的背景色 */
    border-top: 1px solid #e9eef3;
    border-bottom: 1px solid #e9eef3;
    margin-top: 5px; /* 和上方的图标保持一点距离 */
}

.post-likes-section .like-icon {
    width: 16px;
    height: 16px;
    fill: currentColor; /* 让SVG图标继承父元素的颜色 */
    flex-shrink: 0; /* 防止图标被压缩 */
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === @提及 弹出菜单样式 === */
.at-mention-popup {
    position: absolute; /* 相对于父元素定位 */
    bottom: 100%; /* 显示在输入框的上方 */
    left: 40px; /* 和输入框左侧对齐 (考虑了头像宽度) */
    width: calc(100% - 40px); /* 宽度和输入框差不多 */
    max-height: 120px;
    overflow-y: auto;
    background-color: var(--secondary-bg);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    box-shadow: 0 -4px 12px rgba(0,0,0,0.1);
    z-index: 10;
    display: none; /* 默认隐藏 */
}

.at-mention-item {
    padding: 8px 12px;
    font-size: 14px;
    cursor: pointer;
    color: var(--text-primary);
    border-bottom: 1px solid #f0f0f0;
}

.at-mention-item:last-child {
    border-bottom: none;
}

.at-mention-item:hover {
    background-color: #f5f5f5;
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* ▼▼▼ 请用下面这段【新样式】替换掉你现有的 #favorites-list 样式 ▼▼▼ */

/* 让收藏视图成为一个flex容器, 从上到下排列 */
#favorites-view {
    display: flex;
    flex-direction: column;
}

/* 确保收藏页的header高度固定，不被压缩 */
#favorites-view > .header {
    flex-shrink: 0;
}

/* === 收藏列表样式 (修正后) === */
#favorites-list {
    flex-grow: 1; 
    overflow-y: auto; 
    overflow-x: hidden; /* <-- 新增这行，禁止水平滚动 */
    padding: 15px; 
    display: flex;
    flex-direction: column;
    gap: 15px; 
}

/* ▲▲▲ 替换结束 ▲▲▲ */

.favorite-item-card {
    background-color: var(--secondary-bg);
    border-radius: 12px;
    padding: 15px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.06);
    position: relative; /* 为了定位删除按钮 */
}

/* 卡片头部，包含头像、名字和来源 */
.fav-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.fav-card-header .avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
}

.fav-card-header .info {
    flex-grow: 1;
}

.fav-card-header .name {
    font-weight: 600;
    font-size: 15px;
}

.fav-card-header .source {
    font-size: 12px;
    color: var(--text-secondary);
}

/* 卡片内容 */
.fav-card-content {
    font-size: 14px;
    line-height: 1.6;
    color: #333;
    white-space: pre-wrap;
    word-break: break-word;
}

.fav-card-content .chat-image {
    margin-top: 8px; /* 图片和文字的间距 */
}

/* 删除按钮 */
.fav-delete-btn {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 28px;
    height: 28px;
    background: #f0f2f5;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    font-size: 18px;
    color: var(--text-secondary);
    line-height: 28px;
    text-align: center;
}

.fav-delete-btn:hover {
    background-color: #e9ecef;
    color: #ff3b30;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 搜索栏样式 === */
.search-bar-container {
    padding: 10px 15px;
    background-color: #f9f9f9; /* 和列表背景色保持一致 */
    position: relative; /* 为了定位清除按钮 */
    flex-shrink: 0;
}

#favorites-search-input {
    width: 100%;
    padding: 10px 30px 10px 15px; /* 右侧留出清除按钮的位置 */
    font-size: 14px;
    border: 1px solid var(--border-color);
    border-radius: 18px; /* 圆角矩形，更现代化 */
    background-color: var(--secondary-bg);
    box-sizing: border-box;
    outline: none;
}
#favorites-search-input:focus {
    border-color: var(--accent-color);
}

.search-clear-btn {
    position: absolute;
    right: 25px;
    top: 50%;
    transform: translateY(-50%);
    background: #ccc;
    color: white;
    border: none;
    border-radius: 50%;
    width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    font-size: 16px;
    cursor: pointer;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* === 聊天界面多选操作栏优化 === */
#chat-interface-screen .header .selection-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}

#chat-interface-screen .selection-controls .action-btn {
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    padding: 5px;
}

/* === 收藏页面多选模式样式 === */
#favorites-view.selection-mode .favorite-item-card {
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* 选择框的样式 */
.favorite-item-card::before {
    content: '';
    position: absolute;
    left: -25px; /* 把它放在卡片左边外面 */
    top: 50%;
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    border: 2px solid #ccc;
    border-radius: 50%;
    background-color: white;
    transition: all 0.2s ease;
    opacity: 0; /* 默认隐藏 */
}

/* 进入选择模式时，卡片向右移动，露出选择框 */
#favorites-view.selection-mode .favorite-item-card {
    transform: translateX(35px);
}
#favorites-view.selection-mode .favorite-item-card::before {
    opacity: 1;
}

/* 选中后的样式 */
#favorites-view.selection-mode .favorite-item-card.selected::before {
    background-color: var(--accent-color);
    border-color: var(--accent-color);
    content: '✔';
    color: white;
    font-size: 14px;
    text-align: center;
    line-height: 20px;
}

/* 底部操作栏 (终极修正版) */
#favorites-action-bar {
    position: absolute; /* ★ 改为 absolute，相对于 #phone-screen 定位 */
    bottom: 0;
    left: 0;
    right: 0;           /* ★ 新增 right: 0，和 left: 0 一起撑满宽度 */
    width: auto;        /* ★ 改为 auto，让 left/right 决定宽度 */
    padding: 10px 15px;
    padding-bottom: calc(10px + env(safe-area-inset-bottom)); /* 适配iPhone底部安全区 */
    background-color: rgba(247, 247, 247, 0.9);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-top: 1px solid var(--border-color);
    box-sizing: border-box;
    z-index: 5;
    display: none;
    /* max-width 已经不需要了，因为父元素已经限制了宽度 */
}

#favorites-action-bar .action-bar-btn {
    width: 100%;
    padding: 12px;
    border-radius: 8px;
    border: none;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    background-color: #ff3b30;
    color: white;
}

/* === 【修正】聊天界面头部控件切换逻辑 === */

/* 默认状态：隐藏多选控件 */
#chat-interface-screen .header .selection-controls {
    display: none;
}

/* 默认状态：显示默认控件，并让它撑满整个头部 */
#chat-interface-screen .header .default-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}

/* 当进入多选模式时：隐藏默认控件 */
#chat-interface-screen.selection-mode .header .default-controls {
    display: none;
}

/* 当进入多选模式时：显示多选控件，并让它撑满整个头部 */
#chat-interface-screen.selection-mode .header .selection-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 修正：放大所有主要的“+”号按钮 === */
#add-chat-btn,
#add-world-book-btn,
#create-album-btn-page {
    font-size: 28px;   /* 显著增大字体大小，使其视觉上与旁边的图标匹配 */
    font-weight: 300;  /* 使用更细的字重，让加号看起来更清爽，不显粗笨 */
    position: relative;/* 允许进行位置微调 */
    top: -1px;         /* 字体放大后，通常需要稍微向上移动一点，使其视觉上更居中 */
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这些新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* 预览区容器样式 */
#settings-preview-area {
    width: 100%;
    height: 180px; /* 给一个固定的高度 */
    background-color: #f0f2f5;
    border-radius: 8px;
    padding: 15px;
    box-sizing: border-box;
    overflow: hidden; /* 防止内容溢出 */
    display: flex;
    flex-direction: column;
    gap: 10px; /* 预览气泡之间的间距 */
    border: 1px solid var(--border-color);
    position: relative; /* 为了定位背景 */
}

/* 预览区的背景，可以和真实聊天界面同步 */
#settings-preview-area::before {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 100%; height: 100%;
    background-size: cover;
    background-position: center;
    z-index: 1;
    opacity: 0.8;
}

/* 让预览气泡在背景之上 */
#settings-preview-area .message-wrapper {
    position: relative;
    z-index: 2;
}

/* 预览区内使用的头像要小一点 */
#settings-preview-area .message-bubble .avatar {
    width: 30px;
    height: 30px;
}

#settings-preview-area .message-bubble .timestamp {
    display: none; /* 预览区不需要显示时间戳 */
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */
.existing-group-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px;
    background-color: #f9f9f9;
    border-radius: 8px;
    border: 1px solid var(--border-color);
}

.existing-group-item .group-name {
    font-weight: 500;
}

.existing-group-item .delete-group-btn {
    color: #ff3b30;
    font-size: 20px;
    cursor: pointer;
    padding: 5px;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */
.chat-group-container {
    border-bottom: 1px solid var(--border-color);
}
.chat-group-container:first-child {
    border-top: 1px solid var(--border-color);
}

.chat-group-header {
    display: flex;
    align-items: center;
    padding: 12px 15px;
    cursor: pointer;
    background-color: #f7f7f7;
}

.chat-group-header .arrow {
    font-size: 14px;
    margin-right: 8px;
    transition: transform 0.2s ease;
}

.chat-group-header.collapsed .arrow {
    transform: rotate(-90deg);
}

.chat-group-header .group-name {
    font-weight: 600;
    font-size: 15px;
}

.chat-group-content {
    max-height: 1000px; /* 一个足够大的值 */
    overflow: hidden;
    transition: max-height 0.3s ease-in-out;
}

.chat-group-content.collapsed {
    max-height: 0;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */

/* 格式助手按钮的容器 */
.format-helpers {
    display: flex;
    gap: 10px;
    margin-bottom: 15px; /* 与下方的文本框拉开距离 */
    flex-wrap: wrap; /* 如果按钮太多可以换行 */
}

/* 单个格式助手按钮的样式 */
.format-btn {
    background-color: #e9ecef;
    color: var(--text-primary);
    border: none;
    padding: 6px 12px;
    border-radius: 16px; /* 胶囊形状，更友好 */
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.2s;
}

.format-btn:hover {
    background-color: #dcdfe3;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */

/* “…”按钮的样式 */
.post-actions-btn {
    margin-left: auto; /* 关键：让它自动靠到最右边 */
    padding: 5px 10px;
    font-size: 20px;
    font-weight: bold;
    color: var(--text-secondary);
    cursor: pointer;
    border-radius: 50%;
    line-height: 1;
}
.post-actions-btn:hover {
    background-color: #f0f0f0;
}

/* 动态编辑模态框的样式 (它将复用现有的操作菜单样式) */
#post-actions-modal .custom-modal-footer button {
    width: 100%;
    border: none;
    border-bottom: 1px solid #dbdbdb;
    padding: 14px;
    font-size: 18px;
}
#post-actions-modal .custom-modal-footer button:last-child {
    border-bottom: none;
}
#post-actions-modal #cancel-post-action-btn {
    margin-top: 8px;
    border-radius: 8px;
    background-color: #f0f0f0;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* 统一重置转账卡片内所有文字的特效和颜色 */
#chat-messages .transfer-card .transfer-title,
#chat-messages .transfer-card .transfer-amount,
#chat-messages .transfer-card .transfer-note {
    text-shadow: none !important; /* 强制移除任何发光或阴影效果 */
    color: white !important;      /* 强制锁定文字颜色为白色 */
}

/* 分别锁定各自的字体大小和字重，防止被篡改 */
#chat-messages .transfer-card .transfer-title {
    font-size: 16px !important;
    font-weight: 600 !important;
}

#chat-messages .transfer-card .transfer-amount {
    font-size: 28px !important;
    font-weight: bold !important;
}

#chat-messages .transfer-card .transfer-note {
    font-size: 13px !important;
    opacity: 0.9 !important;
}

/* ▼▼▼ 这是新增的样式，用于修正所有头部标题的居中问题 ▼▼▼ */
.header > span:nth-child(2),
#chat-header-title {
    position: absolute;
    left: 50%;
    transform: translateX(calc(-50% - 2px)); /* 在-50%的基础上，再向左推2像素 */
    
    /* (可选但推荐) 防止长标题与两边按钮重叠 */
    max-width: 60%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* ▲▲▲ 粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】可视化消息编辑器样式 ▼▼▼ */
#message-editor-container {
    display: flex;
    flex-direction: column;
    gap: 15px;
}

.message-editor-block {
    background-color: #f9f9f9;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 12px;
}

.message-editor-block textarea {
    width: 100%;
    min-height: 60px;
    resize: vertical;
    border: 1px solid #ccc;
    border-radius: 6px;
    padding: 8px;
    font-size: 14px;
    box-sizing: border-box;
}

.message-editor-block .format-helpers {
    margin-top: 8px;
    margin-bottom: 0; /* 覆盖默认的 margin-bottom */
}

.message-editor-block .delete-block-btn {
    float: right;
    margin-top: -5px;
    background: none;
    border: none;
    color: #ff3b30;
    font-size: 20px;
    cursor: pointer;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】联系人选择器样式 ▼▼▼ */
.contact-picker-item {
    display: flex;
    align-items: center;
    padding: 10px 15px;
    cursor: pointer;
    border-bottom: 1px solid var(--border-color);
}
.contact-picker-item .checkbox {
    width: 20px;
    height: 20px;
    border: 2px solid #ccc;
    border-radius: 50%;
    margin-right: 15px;
    transition: all 0.2s ease;
}
.contact-picker-item.selected .checkbox {
    background-color: var(--accent-color);
    border-color: var(--accent-color);
    content: '✔';
    color: white;
    font-size: 14px;
    text-align: center;
    line-height: 20px;
}
.contact-picker-item .avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 12px;
}
.contact-picker-item .name {
    font-weight: 500;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】群成员管理界面样式 ▼▼▼ */
#member-management-list {
    padding: 0; /* 移除默认padding，让列表项撑满 */
}

.member-management-item {
    display: flex;
    align-items: center;
    padding: 12px 15px;
    border-bottom: 1px solid var(--border-color);
}

.member-management-item .avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 12px;
}

.member-management-item .name {
    flex-grow: 1;
    font-weight: 500;
}

.member-management-item .remove-member-btn {
    background-color: #ff3b30;
    color: white;
    border: none;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    font-size: 20px;
    line-height: 28px;
    text-align: center;
    cursor: pointer;
    flex-shrink: 0;
}

#member-management-actions {
    flex-shrink: 0;
    padding: 15px;
    border-top: 1px solid var(--border-color);
    background-color: #f7f7f7;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

#member-management-actions button {
    width: 100%;
    padding: 15px;
    background-color: var(--accent-color);
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
}
#member-management-actions #create-new-member-btn {
    background-color: #4cd964; /* 新建用绿色，以示区分 */
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】外卖代付卡片样式 ▼▼▼ */
.message-bubble.is-waimai-request .content {
    padding: 0;
    background: transparent;
    box-shadow: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.waimai-card {
    width: 240px;
    border-radius: 12px;
    overflow: hidden;
    background-color: #fff;
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

.waimai-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid #f0f0f0;
}

.waimai-header .icon {
    width: 20px;
    height: 20px;
}

.waimai-header .title-group {
    display: flex;
    align-items: baseline;
    font-size: 14px;
    color: #8a8a8a;
}
.waimai-header .title-group .brand {
    font-weight: 600;
    color: #555;
    margin-right: 5px;
}
.waimai-header .title-group .separator {
    margin: 0 5px;
}

.waimai-catchphrase {
    font-size: 13px;
    color: #1f1f1f;
    padding: 12px;
}

.waimai-main {
    background-color: #FFD66B; /* 橙黄色背景 */
    padding: 12px;
    text-align: center;
}

.waimai-main .request-title {
    font-size: 12px;
    color: #856404;
    margin-bottom: 8px;
}

.waimai-main .payment-box {
    background-color: #fff;
    border-radius: 8px;
    padding: 15px 10px;
}

.waimai-main .payment-label {
    font-size: 13px;
    color: #8a8a8a;
}

.waimai-main .amount {
    font-size: 32px;
    font-weight: 700;
    color: #1f1f1f;
    margin: 4px 0 12px 0;
}

.waimai-main .countdown-label {
    font-size: 13px;
    color: #8a8a8a;
}
.waimai-main .countdown-timer {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-left: 5px;
}
.waimai-main .countdown-timer span {
    background-color: #333;
    color: white;
    padding: 2px 4px;
    border-radius: 2px;
    font-weight: bold;
    font-size: 12px;
}

.waimai-details-btn {
    width: 100%;
    padding: 10px 0;
    margin-top: 15px;
    border: none;
    border-radius: 6px;
    background-color: #FFC33A;
    color: #49380a;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】外卖响应状态样式 ▼▼▼ */

/* === 同意支付后的样式 === */
.message-bubble.status-paid .waimai-card {
    border: 2px solid #28a745; /* 绿色边框 */
}
.message-bubble.status-paid .waimai-main .request-title::before {
    content: '✅  ';
}
.message-bubble.status-paid .waimai-main .request-title {
    color: #155724;
    font-weight: 600;
    /* 重写 request-title 的内容 */
    content: "我已为您买单，请尽情享用吧～" !important;
    display: block;
    margin-bottom: 15px;
}

.message-bubble.status-paid .payment-box {
    display: none; /* 隐藏支付详情 */
}
.message-bubble.status-paid .waimai-details-btn {
    background-color: #28a745;
    color: white;
}

/* === 拒绝支付后的样式 === */
.message-bubble.status-rejected .waimai-card {
    border: 2px solid #dc3545; /* 红色边框 */
    opacity: 0.8;
}
.message-bubble.status-rejected .waimai-main {
    background-color: #e9ecef;
}
.message-bubble.status-rejected .waimai-main .request-title::before {
    content: '❌ ';
}
.message-bubble.status-rejected .waimai-main .request-title {
    color: #721c24;
    font-weight: 600;
    /* 重写 request-title 的内容 */
    content: "我拒绝了您的代付请求" !important;
    display: block;
    margin-bottom: 15px;
}
.message-bubble.status-rejected .payment-box {
    display: none; /* 隐藏支付详情 */
}
 .message-bubble.status-rejected .waimai-details-btn {
    background-color: #6c757d;
    color: white;
}

/* 强制重写 request-title 内容的技巧 */
.message-bubble[class*="status-"] .request-title {
    font-size: 0; /* 隐藏原始文本 */
}
.message-bubble[class*="status-"] .request-title::after {
    font-size: 14px; /* 让伪元素显示新文本 */
}
.message-bubble.status-paid .request-title::after {
    content: "我已为您买单，请尽情享用吧～";
}
.message-bubble.status-rejected .request-title::after {
    content: "我拒绝了您的代付请求";
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】外卖请求的用户操作按钮样式 ▼▼▼ */
.waimai-user-actions {
    display: flex;
    gap: 10px;
    padding: 0 12px 12px 12px; /* 在卡片底部留出空间 */
    background-color: #fff;
}

.waimai-user-actions button {
    flex: 1;
    padding: 10px;
    border-radius: 8px;
    border: 1.5px solid;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.waimai-pay-btn {
    background-color: #28a745;
    border-color: #1f7a33;
    color: white;
}
.waimai-pay-btn:hover {
    background-color: #218838;
}

.waimai-decline-btn {
    background-color: #f8f9fa;
    border-color: #ced4da;
    color: #495057;
}
.waimai-decline-btn:hover {
    background-color: #e2e6ea;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* === 【新增】统一设置页面的背景色 (已修正) === */
#api-settings-screen,
#font-settings-screen,
#wallpaper-screen,
#memories-view,
#contact-picker-screen,
#member-management-screen,
#world-book-editor-screen {  
    background-color: var(--secondary-bg);
}

/* 确保这些页面的内容区能正确滚动 */
#api-settings-screen .form-container,
#font-settings-screen .form-container,
#wallpaper-screen .form-container {
    padding-top: 100px;
    margin-top: -80px;
    background-color: var(--secondary-bg);
}

/* 壁纸设置页面的预览区比较特殊，需要额外调整 */
#wallpaper-screen .form-container {
    align-items: center; /* 保持内容居中 */
}

/* ▼▼▼ 【全新】来电请求与视频通话界面样式 ▼▼▼ */

/* --- 来电请求模态框 --- */
#incoming-call-modal .incoming-call-content {
    background-color: rgba(40, 40, 40, 0.85);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-radius: 20px;
    width: 280px;
    padding: 30px 20px;
    text-align: center;
    color: white;
    box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}

.caller-avatar {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    object-fit: cover;
    margin-bottom: 12px;
    border: 3px solid rgba(255,255,255,0.5);
}

.caller-name {
    font-size: 20px;
    font-weight: 600;
    margin-bottom: 5px;
}

.caller-text {
    font-size: 14px;
    color: #ccc;
    margin-bottom: 30px;
}

.incoming-call-actions {
    display: flex;
    justify-content: space-around;
    align-items: center;
}

.action-button-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: #e0e0e0;
}

.call-action-btn {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    background-size: 50%;
    background-repeat: no-repeat;
    background-position: center;
    transition: transform 0.2s, box-shadow 0.2s;
    box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
.call-action-btn:active {
    transform: scale(0.9);
}

.call-action-btn.decline {
    background-color: #ff3b30;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13.5 16.5L3 6m18 6l-5.6-5.6a1.2 1.2 0 0 0-1.7 0L3 18.2a1.2 1.2 0 0 0-.3 1.2l1.2 3.6a1.2 1.2 0 0 0 1.2.9h15.6a1.2 1.2 0 0 0 1.2-1.2V7.7a1.2 1.2 0 0 0-.3-1.1z"/></svg>');
}

.call-action-btn.accept {
    background-color: #4cd964;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>');
    animation: pulse 1.5s infinite;
}

@keyframes pulse {
    0% { box-shadow: 0 0 0 0 rgba(76, 217, 100, 0.7); }
    70% { box-shadow: 0 0 0 15px rgba(76, 217, 100, 0); }
    100% { box-shadow: 0 0 0 0 rgba(76, 217, 100, 0); }
}

/* --- 视频通话界面 --- */
/* ▼▼▼ 请用这一整块【最终修正版】的代码，替换所有旧的 video-call 相关CSS ▼▼▼ */

/* 1. 通话屏幕总容器 (保持不变) */
#video-call-screen {
    background-color: #1c1c1e;
    color: white;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* 2. 顶部栏和底部控制栏 (保持不变) */
.video-call-top-bar {
    position: absolute;
    top: 0; left: 0; width: 100%;
    padding: 15px 20px;
    padding-top: 50px;
    background: linear-gradient(to bottom, rgba(0,0,0,0.5), transparent);
    z-index: 10;
    text-align: center;
    box-sizing: border-box;
    pointer-events: none;
}
#call-timer {
    font-size: 16px;
    font-weight: 500;
    letter-spacing: 1px;
}
.video-call-controls {
    position: absolute;
    bottom: 0; left: 0; width: 100%;
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 20px;
    padding-bottom: 40px;
    background: linear-gradient(to top, rgba(0,0,0,0.5), transparent);
    z-index: 10;
    box-sizing: border-box;
}

/* 3. 参与者头像显示区 (保持不变) */
.video-call-avatar-area {
    flex-grow: 1; 
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
    padding-top: 80px; /* 确保顶部有足够空间 */
    box-sizing: border-box;
    overflow-y: auto; /* ★ 新增：如果头像太多，允许此区域滚动 */
}

/* 4. 头像网格容器 (保持不变) */
#participant-avatars-grid {
    display: flex;
    flex-wrap: wrap; 
    justify-content: center;
    align-items: center;
    gap: 15px; /* ★ 稍微减小头像间距 */
    max-width: 100%;
}

/* 5. 单个参与者的头像容器 (头像缩小) */
.participant-avatar-wrapper {
    position: relative;
    text-align: center;
    flex-shrink: 0;
}
.participant-avatar {
    width: 70px;   /* ★ 从 80px 缩小到 70px */
    height: 70px;  /* ★ 从 80px 缩小到 70px */
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid rgba(255, 255, 255, 0.2);
    transition: all 0.3s ease;
}
.participant-name {
    margin-top: 8px;
    font-size: 12px;
    color: #ccc;
}

/* 6. 发言者头像高亮效果 (保持不变) */
.participant-avatar.speaking {
    border-color: #4cd964;
    box-shadow: 0 0 20px rgba(76, 217, 100, 0.6);
    transform: scale(1.05);
}

/* 7. 【最终版】对话框区域 */
#video-call-main {
    flex-shrink: 0; 
    height: 30%;   /* ★ 核心修改：高度从35%减小到30% */
    margin: 15px 15px 130px 15px; /* ★ 核心修改：底部边距从120px增加到130px，创造明显空隙 */
    overflow-y: auto;
    padding: 15px;
    background-color: rgba(255, 255, 255, 0.05);
    border-radius: 20px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    box-sizing: border-box;
}

/* 8. 控制按钮样式 (保持不变) */
.control-btn {
    width: 70px;
    height: 70px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    background-repeat: no-repeat;
    background-position: center;
    transition: transform 0.2s, background-color 0.2s;
}
.control-btn:active {
    transform: scale(0.9);
}
.control-btn.speak-btn {
    background-color: rgba(255,255,255,0.2);
    background-size: 55%;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="white" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>');
}
.control-btn.hangup-btn {
    background-color: #ff3b30;
    background-size: 50%;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13.5 16.5L3 6m18 6l-5.6-5.6a1.2 1.2 0 0 0-1.7 0L3 18.2a1.2 1.2 0 0 0-.3 1.2l1.2 3.6a1.2 1.2 0 0 0 1.2.9h15.6a1.2 1.2 0 0 0 1.2-1.2V7.7a1.2 1.2 0 0 0-.3-1.1z"/></svg>');
}
.control-btn.join-btn {
    background-color: #007bff;
    background-size: 50%;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="17" y1="11" x2="23" y2="11"></line></svg>');
}

/* ▲▲▲ 新CSS替换结束 ▲▲▲ */

/* ▼▼▼ 【全新】视频通话对话气泡样式 ▼▼▼ */
.call-message-bubble {
    padding: 10px 15px;
    border-radius: 12px;
    max-width: 85%;
    line-height: 1.6;
    word-break: break-word;
    white-space: pre-wrap;
}

.call-message-bubble.ai-speech {
    background-color: rgba(255, 255, 255, 0.15);
    align-self: flex-start; /* AI发言靠左 */
}

.call-message-bubble.user-speech {
    background-color: #4cd964; /* 用户发言用绿色，类似微信 */
    align-self: flex-end;   /* 用户发言靠右 */
    text-align: left; /* 确保用户气泡内的文字是左对齐的 */
}
/* ▲▲▲ 新增CSS结束 ▲▲▲ */

/* ▼▼▼ 【全新添加】正在呼叫界面样式 ▼▼▼ */
#outgoing-call-screen {
    background-color: #1c1c1e;
    color: white;
    justify-content: center; /* 垂直居中 */
    align-items: center;   /* 水平居中 */
}

.outgoing-call-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.outgoing-call-actions {
    margin-top: 50px; /* 和上方文字拉开距离 */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: #e0e0e0;
}
/* ▲▲▲ 添加结束 ▲▲▲ */

/* 1. 动态帖子的外层容器，我们需要它来定位和裁剪 */
.qzone-post-container {
    position: relative; /* 让内部的删除按钮可以相对于它定位 */
    overflow: hidden;   /* 隐藏掉超出部分的删除按钮 */
    border-radius: 12px;/* 和内部卡片保持一致的圆角 */
}

/* 2. 可滑动的内容卡片，增加一个平滑的过渡效果 */
.qzone-post-item {
    transition: transform 0.3s ease;
    background-color: var(--secondary-bg); /* 确保它有背景色，能盖住下面的删除按钮 */
    position: relative; /* 确保它在最上层 */
    z-index: 2;
}

/* 3. 【核心】这就是那个“删除”按钮的样式！*/
.qzone-post-delete-action {
    position: absolute; /* 绝对定位，脱离文档流 */
    top: 0;
    right: 0;
    bottom: 0;
    width: 90px; /* 删除按钮的宽度 */
    background-color: #ff3b30; /* 您想要的红色背景 */
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 500;
    cursor: pointer;
    z-index: 1; /* 确保它在卡片下面 */
}

/* 4. 当卡片左滑时，把它向左移动，露出删除按钮 */
.qzone-post-item.swiped {
    transform: translateX(-90px); /* 移动的距离和删除按钮的宽度一致 */
}

/* ▲▲▲ 粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这【一整块】全新的“拍一拍”样式，粘贴到 <style> 的末尾 ▼▼▼ */

/* 1. “拍一拍”的屏幕震动动画 */
@keyframes pat-shake {
    0%, 100% { transform: translateX(0); }
    10%, 30%, 50%, 70%, 90% { transform: translateX(-3px); }
    20%, 40%, 60%, 80% { transform: translateX(3px); }
}

.pat-animation {
    animation: pat-shake 0.4s ease-in-out;
}

/* 2. “拍一拍”系统提示消息的样式 */
.system-message {
    align-self: center; /* 居中显示 */
    padding: 4px 12px;
    margin: 5px 0;
    background-color: rgba(0, 0, 0, 0.1);
    color: var(--text-secondary);
    font-size: 12px;
    border-radius: 10px;
    text-align: center;
    max-width: 80%;
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* 让“拍一拍”类型的 wrapper 居中 */
.message-wrapper.system-pat {
    justify-content: center;
    align-self: center;
    margin: 5px 0;
    max-width: 80%;
}
/* “拍一-拍”消息气泡的样式 */
.message-bubble.system-bubble {
    background-color: rgba(0, 0, 0, 0.1);
    color: var(--text-secondary);
    font-size: 12px;
    padding: 4px 12px;
    border-radius: 10px;
}

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */

/* === 修正：让顶部操作栏可以横向滚动 === */
#chat-input-actions-top {
    display: flex;
    gap: 8px;
    padding: 0 5px;

    /* --- 核心代码开始 --- */
    overflow-x: auto;      
    flex-wrap: nowrap;     
    -webkit-overflow-scrolling: touch; 

    scrollbar-width: none; 
    -ms-overflow-style: none;  
}

#chat-input-actions-top::-webkit-scrollbar {
    display: none; 
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* === 【全新】聊天界面头部状态栏样式 === */

/* 1. 标题和状态的总容器，使用flex布局让它们垂直排列 */
#chat-header-title-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center; /* 水平居中 */
    gap: 2px; /* 标题和状态之间的微小间距 */
    
    /* 为了让它能在flex布局中正确居中 */
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    max-width: 60%;
}

/* 2. 主标题的样式微调 */
#chat-header-title {
    font-size: 16px; /* 可以稍微缩小一点，给状态栏留出空间 */
    font-weight: 600;
    position: static; /* 覆盖掉旧的absolute定位 */
    transform: none;  /* 覆盖掉旧的transform */
    /* 保证长标题也能正确显示省略号 */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* 3. 状态栏容器 */
#chat-header-status {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    color: var(--text-secondary);
    transition: all 0.3s ease;
}

/* 4. 状态小圆点 */
.status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background-color: #4cd964; /* 默认绿色，代表在线 */
    transition: background-color 0.3s ease;
}

/* 当AI状态为“忙碌”或“离开”时，让圆点变灰色 */
#chat-header-status.busy .status-dot {
    background-color: #cccccc;
}

/* 5. 状态文本 */
.status-text {
    font-weight: 500;
}

/* === 【全新美化版】回忆卡片样式 === */

/* 1. 卡片总容器：这里负责定义整体的背景色和边框 */
.memory-card {
    background-color: #fffaf0; /* 统一的、温暖的米黄色背景 */
    border-radius: 12px;
    padding: 15px; /* 在卡片四周留出内边距 */
    box-shadow: 0 2px 6px rgba(0,0,0,0.07);
    border-left: 5px solid #ffb74d; 
    display: flex; /* 让它成为flex容器，方便内部元素排列 */
    flex-direction: column; /* 让头部和内容垂直堆叠 */
    gap: 8px; /* 在头部和内容之间创造一个自然的间距 */
}

/* 2. 头部容器：现在只负责布局和分割线 */
.memory-card .header {
    border-bottom: 1px solid rgba(217, 129, 0, 0.15); /* 分割线颜色可以稍微加深一点 */
    padding-bottom: 8px; 
}

/* 3. 日期样式 (保持不变) */
.memory-card .header .date {
    font-size: 11px;
    color: #a1887f;
    margin-bottom: 4px; 
}

/* 4. 作者样式 (保持不变) */
.memory-card .header .author {
    font-weight: 600;
    color: #d98100;
    font-size: 15px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 5. 内容区样式 (保持不变) */
.memory-card .content {
    font-size: 14px;
    line-height: 1.7;
    color: #5d4037;
    white-space: pre-wrap;
}

/* === 【全新】约定/倒计时卡片样式 === */
.countdown-card {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    border-radius: 12px;
    padding: 20px;
    box-shadow: 0 4px 15px rgba(118, 75, 162, 0.4);
    text-align: center;
    position: relative;
    overflow: hidden;
    flex-shrink: 0;
}
.countdown-card::before {
    content: '✨';
    position: absolute;
    top: -10px;
    left: -10px;
    font-size: 50px;
    opacity: 0.1;
    transform: rotate(-15deg);
}
.countdown-card .title {
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 15px;
}
.countdown-card .timer {
    font-size: 28px;
    font-weight: 300;
    letter-spacing: 2px;
    margin-bottom: 15px;
}
.countdown-card .target-date {
    font-size: 12px;
    opacity: 0.8;
    border-top: 1px solid rgba(255,255,255,0.2);
    padding-top: 10px;
}

/* === 【全新】聊天锁定遮罩层样式 === */
#chat-lock-overlay {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    background-color: rgba(247, 247, 247, 0.9);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    z-index: 150; /* 比输入框高，比贴纸面板低 */
    display: none; /* 默认隐藏 */
    align-items: center;
    justify-content: center;
    padding: 20px;
    box-sizing: border-box;
    border-top: 1px solid var(--border-color);
    text-align: center;
}
#chat-lock-content {
    display: flex;
    flex-direction: column;
    gap: 15px;
}
#chat-lock-content .lock-text {
    color: var(--text-secondary);
    font-size: 14px;
}
#chat-lock-content .lock-action-btn {
    padding: 10px 20px;
    border-radius: 20px;
    border: 1px solid var(--accent-color);
    background-color: var(--accent-color);
    color: white;
    cursor: pointer;
}
#chat-lock-content .lock-action-btn.secondary {
    background-color: transparent;
    color: var(--accent-color);
}

/* ▼▼▼ 【全新】红包卡片样式 ▼▼▼ */
.message-bubble.is-red-packet .content {
    padding: 0;
    background: transparent;
    box-shadow: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.red-packet-card {
    width: 220px;
    border-radius: 8px;
    background: linear-gradient(160deg, #F96259, #E44D44);
    color: #ffd700; /* 金色文字 */
    padding: 12px;
    cursor: pointer;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    position: relative;
    overflow: hidden;
}

.red-packet-card.opened {
    background: linear-gradient(160deg, #d3c4a0, #c4b693);
    cursor: default;
}

.red-packet-card::before {
    content: '🧧';
    position: absolute;
    top: -5px;
    left: -5px;
    font-size: 30px;
    opacity: 0.2;
    transform: rotate(-10deg);
}

.rp-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

.rp-icon {
    width: 20px;
    height: 20px;
}

.rp-greeting {
    font-size: 15px;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rp-type {
    font-size: 11px;
    color: white;
    opacity: 0.8;
    border-top: 1px solid rgba(255, 255, 255, 0.2);
    padding-top: 8px;
    margin-top: 8px;
}

.rp-claimed-info {
    font-size: 13px;
    color: white;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(255,255,255,0.3);
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】红包详情列表样式 ▼▼▼ */
.rp-details-item {
    display: flex;
    align-items: center;
    padding: 8px 0;
    border-bottom: 1px solid #eee;
}
.rp-details-item:last-child {
    border-bottom: none;
}
.rp-details-item .name {
    flex-grow: 1;
    font-weight: 500;
    color: #333;
}
.rp-details-item .amount {
    font-weight: 500;
    color: #555;
}
.rp-details-item .lucky-king-tag {
    font-size: 10px;
    background-color: #ffd700;
    color: #a67c00;
    padding: 2px 5px;
    border-radius: 4px;
    margin-left: 8px;
    font-weight: bold;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】投票功能样式 ▼▼▼ */

/* 投票卡片在消息气泡中的样式 */
.message-bubble.is-poll .content {
    padding: 0;
    background: transparent;
    box-shadow: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* 投票卡片主体 */
.poll-card {
    width: 250px;
    background-color: #f9f9f9;
    border-radius: 10px;
    border: 1px solid #e0e0e0;
    padding: 12px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.poll-card.closed {
    background-color: #e9ecef; /* 结束后变灰 */
}

/* 投票问题 */
.poll-question {
    font-weight: 600;
    font-size: 15px;
    margin-bottom: 12px;
    line-height: 1.4;
    word-break: break-word;
}

/* 投票选项列表 */
.poll-options-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* 单个投票选项 */
.poll-option-item {
    background-color: white;
    border: 1px solid #dcdcdc;
    border-radius: 8px;
    padding: 10px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: background-color 0.2s;
}

.poll-card:not(.closed) .poll-option-item:hover {
    background-color: #f0f8ff;
}

/* 用户已投票的选项样式 */
.poll-option-item.voted {
    border-color: var(--accent-color);
    background-color: #e7f3ff;
    font-weight: 500;
}

/* 投票进度条 */
.poll-option-bar {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    background-color: rgba(0, 123, 255, 0.1);
    z-index: 1;
    transition: width 0.3s ease-in-out;
}

/* 选项内容（文字和票数），确保在进度条之上 */
.poll-option-content {
    position: relative;
    z-index: 2;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.poll-option-text {
    font-size: 14px;
}

.poll-option-votes {
    font-size: 13px;
    color: #8a8a8a;
    font-weight: 500;
}

/* 投票卡片底部 */
.poll-footer {
    margin-top: 12px;
    padding-top: 8px;
    border-top: 1px solid #e9e9e9;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 12px;
    color: var(--text-secondary);
}

.poll-total-votes {
    font-weight: 500;
}

.poll-action-btn {
    background: none;
    border: 1px solid var(--accent-color);
    color: var(--accent-color);
    padding: 4px 10px;
    border-radius: 15px;
    cursor: pointer;
    font-size: 12px;
}
.poll-card.closed .poll-action-btn {
    background-color: #6c757d;
    color: white;
    border-color: #6c757d;
}

/* 创建投票模态框的选项输入 */
.poll-option-input-wrapper {
    display: flex;
    align-items: center;
    gap: 8px;
}
.poll-option-input-wrapper input {
    flex-grow: 1;
}
.poll-option-input-wrapper .remove-option-btn {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background-color: #f0f0f0;
    color: #ff3b30;
    border: none;
    cursor: pointer;
    font-size: 18px;
    line-height: 28px;
    text-align: center;
    flex-shrink: 0;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* === 【全新】聊天头部“正在输入”状态样式 === */
#chat-header-title.typing-status {
    color: var(--text-secondary);
    animation: typing-pulse 1.5s infinite;
    font-style: italic;
}

@keyframes typing-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
}

#chat-header-title {
    transition: opacity 0.2s ease-in-out;
}

@keyframes message-pop-in {
  from {
    opacity: 0;
    transform: translateY(15px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.message-wrapper.animate-in {
  animation: message-pop-in 0.3s cubic-bezier(0.25, 0.1, 0.25, 1) forwards;
  }

/* ▼▼▼ 【全新】App图标设置样式 ▼▼▼ */
#icon-settings-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
    gap: 20px;
    width: 100%;
    padding: 0 10px;
    box-sizing: border-box;
}

.icon-setting-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.icon-preview {
    width: 60px;
    height: 60px;
    border-radius: 15px;
    background-size: cover;
    background-position: center;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.change-icon-btn {
    padding: 4px 10px;
    font-size: 12px;
    border: 1px solid #ccc;
    background-color: #f0f0f0;
    border-radius: 5px;
    cursor: pointer;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】外观设置页面布局修正 ▼▼▼ */

/* 1. 修正滚动问题 */
#wallpaper-screen .form-container {
    /* 核心修正1: 解决flex布局下的滚动冲突，让滚动条能正常出现 */
    min-height: 0; 
}

/* 2. 修正壁纸预览被压扁的问题 */
#wallpaper-preview {
    /* 核心修正2: 防止预览框被过多的内容挤压变形，让它保持自己的高度 */
    flex-shrink: 0; 
}
/* ▲▲▲ 修正结束 ▲▲▲ */

/* ▼▼▼ 【全新】分享链接功能样式 (无图版) ▼▼▼ */

/* 1. 浏览器界面背景色和内容区样式 (保持不变) */
#browser-screen {
    background-color: #f8f9fa;
}
#browser-content {
    padding: 20px;
    font-size: 16px;
    line-height: 1.8;
    color: #333;
    overflow-y: auto;
    background-color: #f8f9fa;
}
#browser-content .article-title {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 10px;
}
#browser-content .article-meta {
    font-size: 13px;
    color: #8a8a8a;
    margin-bottom: 25px;
    padding-bottom: 15px;
    border-bottom: 1px solid #e0e0e0;
}
#browser-content .article-body {
    white-space: pre-wrap;
    word-break: break-word;
}
#browser-content .article-body p {
    margin-bottom: 1em;
}

/* 2. 聊天气泡中的链接卡片样式 (无图版) */
.message-bubble.is-link-share .content {
    padding: 0;
    background: transparent;
    box-shadow: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.link-share-card {
    width: 210px; 
    background-color: #fff;
    border-radius: 10px;
    border: 1px solid #e0e0e0;
    padding: 12px;
    cursor: pointer;
    transition: background-color 0.2s;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.link-share-card:hover {
    background-color: #f9f9f9;
}

.link-share-card .title {
    font-weight: 600;
    font-size: 15px;
    line-height: 1.4;
    color: #1f1f1f;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

.link-share-card .description {
    font-size: 13px;
    color: #8a8a8a;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

.link-share-card .footer {
    display: flex; /* 让图标和文字水平对齐 */
    align-items: center;
    gap: 6px; /* 图标和文字的间距 */
    font-size: 12px;
    color: var(--text-secondary);
    margin-top: 4px; /* 和上面的描述拉开一点距离 */
}
.link-share-card .footer-icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0; /* 防止图标被压缩 */
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* 单条评论的容器，现在需要相对定位 */
.comment-item {
    position: relative;
    padding-right: 25px; /* 在右侧留出删除按钮的空间 */
}

/* 评论删除按钮的样式 */
.comment-delete-btn {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    width: 22px;
    height: 22px;
    line-height: 22px;
    text-align: center;
    border-radius: 50%;
    color: var(--text-secondary);
    font-size: 18px;
    cursor: pointer;
    transition: all 0.2s ease;
    opacity: 0; /* 默认隐藏 */
}

/* 鼠标悬停在评论上时，显示删除按钮 */
.comment-item:hover .comment-delete-btn {
    opacity: 1;
}

.comment-delete-btn:hover {
    background-color: #f0f0f0;
    color: #ff3b30;
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 把这一整块全新的CSS，粘贴到 <style> 的末尾 ▼▼▼ */

/* === 【全新】夜间模式样式 === */

/* 核心：当 #phone-screen 拥有 .dark-mode 类时，激活以下所有样式 */

/* 1. 全局背景和文本颜色 */
#phone-screen.dark-mode {
  --status-bar-text-color: #ffffff;
    --secondary-bg: #1c1c1e; /* 主要卡片背景色 */
    --border-color: #38383a;  /* 边框颜色 */
    --text-primary: #ffffff;   /* 主要文字颜色 */
    --text-secondary: #8d8d92; /* 次要文字/图标颜色 */

}

/* 2. 各个页面的主背景色 */
#phone-screen.dark-mode #chat-list-screen,
#phone-screen.dark-mode #qzone-screen .qzone-content,
#phone-screen.dark-mode #memories-view {
    background-color: #000000;
}

/* 3. 聊天列表 */
#phone-screen.dark-mode #chat-list {
    background-color: #000000;
}
#phone-screen.dark-mode .chat-list-item {
    border-bottom-color: rgba(255, 255, 255, 0.15);
}
#phone-screen.dark-mode .chat-group-header {
    background-color: #1c1c1e; /* 从白色改为深灰色 */
    border-bottom: 1px solid #38383a; /* 给它一个细微的下边框 */
}
#phone-screen.dark-mode .chat-list-item .name,
#phone-screen.dark-mode .chat-group-header .group-name {
    color: #ffffff;
}
#phone-screen.dark-mode .chat-list-item:hover {
    background-color: #1c1c1e;
}

/* 4. 顶部/底部导航栏 */
#phone-screen.dark-mode .header,
#phone-screen.dark-mode .qzone-header {
    background-color: rgba(25, 25, 25, 0.9);
    backdrop-filter: blur(15px);
    -webkit-backdrop-filter: blur(15px);
    border-bottom-color: rgba(255, 255, 255, 0.15);
    color: #ffffff;
}
#phone-screen.dark-mode .header .back-btn,
#phone-screen.dark-mode .header .action-btn,
#phone-screen.dark-mode .header .save-btn {
    color: #ffffff;
}
#phone-screen.dark-mode #chat-list-bottom-nav {
    background-color: rgba(25, 25, 25, 0.9);
    border-top-color: rgba(255, 255, 255, 0.15);
}
#phone-screen.dark-mode .nav-item.active {
    color: #ffffff;
}

/* 5. 聊天界面 */
#phone-screen.dark-mode #chat-input-area {
    background-color: rgba(5, 5, 5, 0.8);
    border-top: none;
}
#phone-screen.dark-mode #chat-input {
    background-color: #3e3e42;
    color: #ffffff;
}
#phone-screen.dark-mode #chat-input::placeholder {
    color: #8d8d92;
}
#phone-screen.dark-mode .chat-action-icon-btn {
    color: #ffffff;
    background-color: rgba(255, 255, 255, 0.1);
    border: none;
}
#phone-screen.dark-mode #send-btn {
    background-color: var(--accent-color);
}

/* 6. 动态 (QZone) 界面 */
#phone-screen.dark-mode .qzone-actions-bar,
#phone-screen.dark-mode .qzone-post-item {
    background-color: #1c1c1e;
    border: 1px solid #333;
    box-shadow: 0 2px 8px rgba(255, 255, 255, 0.05);
}
#phone-screen.dark-mode .action-item:not(:last-child)::after {
    background-color: #333;
}
#phone-screen.dark-mode .post-footer,
#phone-screen.dark-mode .post-likes-section {
    border-top-color: #333;
}
#phone-screen.dark-mode .post-likes-section {
    background-color: rgba(0, 123, 255, 0.1);
}
#phone-screen.dark-mode .comment-input {
    background-color: #333;
    color: #ffffff;
}
#phone-screen.dark-mode .comment-input::placeholder {
    color: #8d8d92;
}
#phone-screen.dark-mode .post-actions-btn:hover {
    background-color: #333;
}
#phone-screen.dark-mode .at-mention-popup {
    background-color: #1c1c1e;
    border-color: #333;
}
#phone-screen.dark-mode .at-mention-item {
    border-bottom-color: #333;
}
#phone-screen.dark-mode .at-mention-item:hover {
    background-color: #333;
}

/* 7. 回忆录界面 */
#phone-screen.dark-mode .memory-card {
    background-color: #1c1c1e;
    border-left-color: #e6a753;
    box-shadow: 0 4px 12px rgba(255, 255, 255, 0.08);
}
#phone-screen.dark-mode .memory-card .header {
    background-color: #2c2c2e;
    border-bottom-color: #38383a;
    margin: -15px -15px 8px -15px;
    padding: 12px 15px;
    border-radius: 12px 12px 0 0;
}
#phone-screen.dark-mode .memory-card .header .date,
#phone-screen.dark-mode .memory-card .header .author,
#phone-screen.dark-mode .memory-card .content {
    color: #e0e0e0;
}

/* 8. 其他设置和列表页 */
#phone-screen.dark-mode #api-settings-screen,
#phone-screen.dark-mode #font-settings-screen,
#phone-screen.dark-mode #wallpaper-screen,
#phone-screen.dark-mode #contact-picker-screen,
#phone-screen.dark-mode #member-management-screen,
#phone-screen.dark-mode #world-book-editor-screen,
#phone-screen.dark-mode #world-book-list,
#phone-screen.dark-mode .list-item:hover,
#phone-screen.dark-mode .list-container,
#phone-screen.dark-mode .form-container {
    background-color: #000000;
}
#phone-screen.dark-mode .form-group input, 
#phone-screen.dark-mode .form-group select, 
#phone-screen.dark-mode .form-group textarea {
    background-color: #1c1c1e;
    color: #ffffff;
    border-color: #38383a;
}
#phone-screen.dark-mode .form-button-secondary {
    background-color: #333;
    border-color: #555;
    color: #fff;
}
#phone-screen.dark-mode #font-preview {
    background-color: #1c1c1e;
    border-color: #38383a;
}
#phone-screen.dark-mode #font-preview p {
    color: #ffffff;
}

/* ▲▲▲ 粘贴结束 ▲▲▲ */

/* ▼▼▼ 把这一整块【全新的修正CSS】，粘贴到 <style> 的末尾 ▼▼▼ */

/* === 【全新】夜间模式视觉修正 === */

/* 1. 修正动态卡片内的文字颜色 */
#phone-screen.dark-mode .qzone-post-item .post-nickname,
#phone-screen.dark-mode .qzone-post-item .post-content {
    color: #f0f0f0; /* 从深灰色改为明亮的浅灰色 */
}

/* 2. 修正收藏卡片内的文字颜色 */
#phone-screen.dark-mode .favorite-item-card .fav-card-header .name,
#phone-screen.dark-mode .favorite-item-card .fav-card-content {
    color: #f0f0f0; /* 同样改为浅灰色 */
}
#phone-screen.dark-mode .favorite-item-card .fav-card-header .source {
    color: #8d8d92; /* 来源文字用次要灰色 */
}

/* 3. 修正收藏页的搜索栏背景和输入框样式 */
#phone-screen.dark-mode .search-bar-container {
    background-color: #000000; /* 容器背景变为纯黑 */
}
#phone-screen.dark-mode #favorites-search-input {
    background-color: #1c1c1e; /* 输入框背景变为深灰 */
    border-color: #38383a;     /* 边框颜色变暗 */
    color: #ffffff;            /* 输入文字变为白色 */
}
#phone-screen.dark-mode #favorites-search-input::placeholder {
    color: #8d8d92; /* 占位符文字颜色变暗 */
}

/* ▲▲▲ 修正CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 把这一整块全新的CSS，粘贴到 <style> 的末尾 ▼▼▼ */

/* === 【全新】iOS风格的Toggle Switch开关样式 === */

/* 1. 开关的容器 */
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 51px;
    height: 31px;
}

/* 2. 隐藏掉原始的 checkbox 输入框 */
.toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

/* 3. 开关的背景（那个椭圆） */
.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #e9e9eb; /* 关闭时的背景色 */
    transition: .4s;
    border-radius: 34px;
}

/* 4. 开关上的圆点 */
.slider:before {
    position: absolute;
    content: "";
    height: 27px;
    width: 27px;
    left: 2px;
    bottom: 2px;
    background-color: white;
    transition: .4s;
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}

/* 5. 【核心】当 checkbox 被选中时（即开启状态） */
input:checked + .slider {
    background-color: #34c759; /* 开启时的背景色（iOS绿色）*/
}

input:checked + .slider:before {
    transform: translateX(20px); /* 让圆点滑动到右边 */
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】引用回复功能样式 ▼▼▼ */

/* 1. 输入框上方的“回复预览栏” */
#reply-preview-bar {
    display: none; /* 默认隐藏 */
    padding: 8px 12px;
    margin: 0 8px 8px 8px; /* 和输入框周围的边距保持一致 */
    background-color: rgba(0, 0, 0, 0.05);
    border-left: 3px solid var(--accent-color);
    border-radius: 6px;
    position: relative;
    font-size: 13px;
    color: var(--text-secondary);
}

#phone-screen.dark-mode #reply-preview-bar {
    background-color: rgba(255, 255, 255, 0.1);
}

.reply-preview-content .sender {
    font-weight: 600;
    color: var(--text-primary);
}

.reply-preview-content .text {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block; /* 确保省略号生效 */
    max-width: 95%;
}

#cancel-reply-btn {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    border-radius: 50%;
    background-color: rgba(0,0,0,0.1);
    cursor: pointer;
    font-size: 14px;
}

/* 2. 消息气泡内部的“引用消息块” */
.quoted-message {
    padding: 6px 10px;
    margin-bottom: 6px;
    background-color: rgba(0, 0, 0, 0.04);
    border-left: 2px solid var(--accent-color);
    border-radius: 4px;
    font-size: 0.9em; /* 字体比正文小一点 */
    opacity: 0.8;
    /* (已移除 overflow: hidden;) */
}

#phone-screen.dark-mode .quoted-message {
    background-color: rgba(255, 255, 255, 0.08);
    border-left-color: #a0cff1;
}

.quoted-message .quoted-sender {
    font-weight: 600;
    color: var(--accent-color);
}
#phone-screen.dark-mode .quoted-message .quoted-sender {
    color: #a0cff1;
}

.quoted-message .quoted-content {
    color: var(--text-secondary);
    white-space: normal;     /* 核心修正1: 允许文本正常换行 */
    word-break: break-word;  /* 核心修正2: 强制长单词或连续字符断开，防止溢出 */
    display: block;
    /* (已移除 overflow 和 text-overflow，因为我们需要多行显示而不是单行省略号) */
}

/* === 字体预览框样式 (修正后) === */

/* 默认（日间模式）的样式 */
#font-preview {
    padding: 20px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    background-color: #f9f9f9; /* 日间模式的浅灰色背景 */
    transition: background-color 0.3s, border-color 0.3s;
}

/* 预览框里的文字颜色，默认是黑色 */
#font-preview p {
    color: var(--text-primary);
}

/* 夜间模式下的修正样式 */
#phone-screen.dark-mode #font-preview {
    background-color: #1c1c1e; /* 深灰色背景 */
    border-color: #38383a;     /* 暗色边框 */
}

/* 夜间模式下，预览框里的文字变为白色 */
#phone-screen.dark-mode #font-preview p {
    color: #ffffff;
}

/* ▼▼▼ 【全新】精致版转账操作弹窗样式 ▼▼▼ */
.transfer-actions-content {
    background-color: #fff0f5; /* 粉嫩的背景色 */
    border-radius: 20px;
    width: 290px;
    padding: 20px;
    box-shadow: 0 5px 25px rgba(255, 105, 180, 0.3); /* 粉色阴影 */
    text-align: center;
    position: relative;
    border: 1px solid #ffcce0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.transfer-actions-header {
    font-size: 20px;
    font-weight: bold;
    color: #a35c7b; /* 深粉色标题 */
    margin-bottom: 15px;
}

.transfer-actions-body p {
    font-size: 15px;
    color: #555;
    margin: 0 0 25px 0;
    line-height: 1.5;
}

.transfer-actions-footer {
    display: flex;
    justify-content: space-between;
    gap: 15px;
}

.transfer-actions-footer .action-btn {
    flex: 1;
    padding: 12px;
    border: none;
    border-radius: 12px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.2s, box-shadow 0.2s;
    color: white;
}

.transfer-actions-footer .action-btn:active {
    transform: scale(0.95);
}

.transfer-actions-footer .action-btn.accept {
    background: linear-gradient(135deg, #ff85b3, #ff69b4);
    box-shadow: 0 4px 10px rgba(255, 105, 180, 0.4);
}

.transfer-actions-footer .action-btn.decline {
    background: linear-gradient(135deg, #c2c2c2, #a0a0a0);
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}

.transfer-actions-content .cancel-btn {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: none;
    background-color: rgba(0, 0, 0, 0.1);
    color: #a35c7b;
    font-size: 20px;
    line-height: 28px;
    cursor: pointer;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新样式粘贴到 <style> 的末尾 ▼▼▼ */

/* === 未读消息红点样式 === */
.unread-count-wrapper {
    flex-shrink: 0;
    width: 40px;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding-top: 20px; /* 让红点和名字差不多高 */
}

.unread-count {
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    background-color: #ff3b30; /* iOS 风格的红色 */
    color: white;
    font-size: 13px;
    font-weight: 500;
    line-height: 20px;
    text-align: center;
    border-radius: 10px; /* 圆角矩形 */
    box-shadow: 0 1px 2px rgba(0,0,0,0.15);
    display: none; /* 默认隐藏 */
    justify-content: center;
    align-items: center;
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】通话记录页面与卡片样式 ▼▼▼ */

/* 确保页面背景色统一 */
#call-history-screen {
    background-color: #f0f2f5;
}

/* 通话记录卡片样式 */
.call-record-card {
    background-color: var(--secondary-bg);
    border-radius: 12px;
    padding: 15px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    border-left: 5px solid var(--accent-color);
}
.call-record-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 12px rgba(0,0,0,0.1);
}

/* 卡片头部：包含日期和时长 */
.call-record-card .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 13px;
    color: var(--text-secondary);
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-color);
}
.call-record-card .card-header .duration {
    font-weight: 500;
    color: var(--text-primary);
}

/* 卡片主体：参与者头像 */
.call-record-card .card-body {
    display: flex;
    align-items: center;
}
.call-record-card .participants-avatars {
    display: flex;
    align-items: center;
}
.call-record-card .participant-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid white;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
/* 让头像有一个漂亮的堆叠效果 */
.call-record-card .participant-avatar:not(:first-child) {
    margin-left: -12px;
}
.call-record-card .participants-names {
    margin-left: 12px;
    font-weight: 600;
    color: var(--text-primary);
    font-size: 15px;
}

/* --- 通话详情弹窗样式 --- */
#transcript-modal-body {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 15px;
}
.transcript-entry {
    padding: 8px 12px;
    border-radius: 10px;
    max-width: 85%;
    line-height: 1.5;
    word-break: break-word;
}
.transcript-entry.user {
    background-color: #dcf8c6; /* 类似微信的绿色 */
    align-self: flex-end;
}
.transcript-entry.assistant {
    background-color: #ffffff;
    align-self: flex-start;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

#chat-list-title {
    cursor: pointer;
}

/* ▼▼▼ 【全新】通话记录卡片美化样式 ▼▼▼ */

.call-record-card .card-body {
    /* 将 body 改为 flex 布局，让标题和参与者信息垂直排列 */
    display: flex;
    flex-direction: column;
    gap: 8px; /* 标题和参与者信息之间的间距 */
}

.call-record-card .custom-title {
    font-size: 16px;
    font-weight: 600; /* 加粗，让它像个标题 */
    color: var(--text-primary);
    padding-bottom: 8px; /* 标题下的留白 */
    border-bottom: 1px solid var(--border-color); /* 在标题下加一条分割线 */
    margin-bottom: 4px; /* 和下面的参与者信息拉开一点距离 */
}

.call-record-card .participants-info {
    /* 这个新容器让头像和“与xx”能水平对齐 */
    display: flex;
    align-items: center;
}

/* 参与者名字的样式微调，让它不那么突出 */
.call-record-card .participants-names {
    margin-left: 12px;
    font-weight: 500; /* 不再加粗 */
    font-size: 14px; /* 稍微小一点 */
    color: var(--text-secondary); /* 使用次要文字颜色 */
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】语音转文字功能样式 ▼▼▼ */

/* 1. 语音文字内容的样式 */
.voice-transcript {
    font-size: 14px;         /* 文字大小 */
    line-height: 1.6;        /* 行高，让多行文本更易读 */
    color: var(--text-secondary); /* 使用次要文字颜色，与语音条区分 */
    padding: 8px 12px;       /* 内边距 */
    margin-top: 6px;         /* 和上方的语音条拉开一点距离 */
    background-color: rgba(0, 0, 0, 0.04); /* 给一个淡淡的背景，更有层次感 */
    border-radius: 6px;      /* 圆角 */
    word-break: break-word;  /* 确保长文本能正常换行 */
    display: none;           /* 默认隐藏 */
}

#phone-screen.dark-mode .voice-transcript {
    background-color: rgba(255, 255, 255, 0.1); /* 夜间模式下的背景色 */
}

/* 2. 旋转加载动画的样式 */
.loading-spinner {
    display: none; /* 默认隐藏 */
    width: 16px;
    height: 16px;
    border: 2px solid rgba(0, 0, 0, 0.2);
    border-top-color: var(--accent-color); /* 旋转部分的颜色 */
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin: 0 8px; /* 和波形图、时长保持一点间距 */
}

/* 3. 定义旋转动画 */
@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】分享记录查看器样式修正 ▼▼▼ */
#shared-history-viewer-content {
    display: flex;
    flex-direction: column; /* 让气泡垂直排列 */
    gap: 20px; /* 在每个气泡之间增加20像素的间距 */
    padding: 15px; /* 在容器四周也增加一些内边距，避免气泡贴边 */
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】播放器和歌词样式 ▼▼▼ */
#music-player-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 50;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding-top: 60px;
    background-color: rgba(0,0,0,0.3);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-50px);
    transition: opacity 0.4s ease-out, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

#music-player-overlay.visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.music-player-window { 
    width: 70%; 
    min-height: 420px;
    background-color: rgba(255, 255, 255, 0.6); 
    backdrop-filter: blur(20px); 
    -webkit-backdrop-filter: blur(20px); 
    border-radius: 25px; 
    box-shadow: 0 8px 32px 0 rgba(25, 25, 25, 0.37); 
    border: 1px solid rgba(255, 255, 255, 0.18); 
    padding: 25px; 
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    color: #1f1f1f; 
    position: relative;
    justify-content: space-between;
    padding-bottom: 15px;
}

.music-player-top-actions {
    position: absolute;
    top: 15px;
    left: 15px;
    right: 15px;
    width: calc(100% - 30px);
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.top-left-cluster {
    display: flex;
    align-items: center;
    gap: 15px;
}
#music-return-btn, #music-exit-btn {
    background: none;
    border: none;
    font-size: 28px;
    font-weight: 300;
    cursor: pointer;
    color: #555;
    padding: 5px;
    line-height: 1;
}
#music-exit-btn {
    font-size: 24px;
    font-weight: 400;
}

.music-progress-bar-container {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 25px;
    margin-bottom: 10px;
}
.time-display {
    font-size: 11px;
    color: #888;
    width: 35px;
    text-align: center;
    flex-shrink: 0;
    font-family: 'SF Mono', 'Menlo', monospace;
}
.progress-bar {
    flex-grow: 1;
    height: 5px;
    background-color: #e5e5e5;
    border-radius: 2.5px;
    cursor: pointer;
}
.progress-bar-fill {
    width: 0%;
    height: 100%;
    background-color: #333;
    border-radius: 2.5px;
}

#music-lyrics-container {
    width: 100%;
    height: 192px;
    overflow: hidden;
    position: relative;
    -webkit-mask-image: linear-gradient(transparent, black 20%, black 80%, transparent);
    mask-image: linear-gradient(transparent, black 20%, black 80%, transparent);
}

#music-lyrics-list {
    display: flex;
    flex-direction: column;
    align-items: center;
    transition: transform 0.5s cubic-bezier(0.25, 0.1, 0.25, 1);
}

.lyric-line {
    padding: 4px 0;
    font-size: 14px;
    color: #666;
    text-align: center;
    line-height: 1.5;
    transition: all 0.5s ease;
    opacity: 0.7;
    transform: scale(0.95);
}

.lyric-line.active {
    font-size: 16px;
    color: #000;
    opacity: 1;
    transform: scale(1);
}

.music-player-controls-wrapper {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.music-controls {
    margin-top: 0;
}

#music-return-btn, #music-exit-btn, #music-playlist-btn {
    position: relative;
}

#music-return-btn { top: -2px; }
#music-playlist-btn { top: -3px; }

.playlist-item-actions {
    display: flex;
    align-items: center;
    gap: 15px;
}
.playlist-action-btn {
    font-size: 18px;
    color: #888;
    cursor: pointer;
    transition: color 0.2s;
}
.playlist-action-btn:hover { color: #000; }
.delete-track-btn { font-size: 24px; color: #ff3b30; }
.delete-track-btn:hover { color: #c00; }
.lyrics-btn { font-weight: 500; }

/* --- 【核心修正】确保头像尺寸 --- */
.message-bubble .avatar {
    width: 34px;
    height: 34px;
    border-radius: 20%;
    object-fit: cover;
    flex-shrink: 0; /* 防止被压缩 */
}

/* ▼▼▼ 【全新】撤回消息样式 ▼▼▼ */

/* 1. 撤回消息的占位符样式 */
.recalled-message-placeholder {
    align-self: center; /* 居中显示 */
    padding: 4px 12px;
    margin: 5px 0;
    background-color: rgba(0, 0, 0, 0.1);
    color: var(--text-secondary);
    font-size: 12px;
    border-radius: 10px;
    text-align: center;
    max-width: 80%;
    cursor: pointer; /* 让它看起来可以点击 */
}

/* 2. 夜间模式下的适配 */
#phone-screen.dark-mode .recalled-message-placeholder {
    background-color: rgba(255, 255, 255, 0.15);
}

/* 3. AI撤回消息时的动画效果 */
@keyframes recall-animation {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(0.8);
  }
}

.message-wrapper.recalled-animation {
  animation: recall-animation 0.3s ease-out forwards;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】撤回消息样式修正 ▼▼▼ */

/* 强制撤回消息的占位符不换行，并保持内容居中 */
.recalled-message-placeholder {
    white-space: nowrap; /* 核心：禁止文本换行 */
    display: inline-block; /* 让背景根据内容自适应宽度 */
    padding: 4px 12px;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】世界书分类列表样式 ▼▼▼ */
.world-book-group-container {
    border-bottom: 1px solid var(--border-color);
}
.world-book-group-container:first-child {
    border-top: 1px solid var(--border-color);
}
.world-book-group-header {
    display: flex;
    align-items: center;
    padding: 12px 15px;
    cursor: pointer;
    background-color: #f7f7f7;
}
.world-book-group-header .arrow {
    font-size: 14px;
    margin-right: 8px;
    transition: transform 0.2s ease;
}
.world-book-group-header.collapsed .arrow {
    transform: rotate(-90deg);
}
.world-book-group-header .group-name {
    font-weight: 600;
    font-size: 15px;
}
.world-book-group-content {
    max-height: 1000px;
    overflow: hidden;
    transition: max-height 0.3s ease-in-out;
}
.world-book-group-content.collapsed {
    max-height: 0;
}
#phone-screen.dark-mode .world-book-group-header {
    background-color: #1c1c1e;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】红包/转账模态框页签样式 ▼▼▼ */
.frame-tabs {
    display: flex;
    background-color: #f0f0f0;
    padding: 4px;
    margin: 15px;
    border-radius: 8px;
}
.frame-tab {
    flex: 1;
    text-align: center;
    padding: 8px;
    font-size: 14px;
    font-weight: 500;
    color: #555;
    cursor: pointer;
    border-radius: 6px;
    transition: all 0.2s ease-in-out;
}
.frame-tab.active {
    background-color: #ffffff;
    color: #000000;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段全新的CSS粘贴到 <style> 的末尾 ▼▼▼ */

/* 1. 分类文件夹的样式 */
.wb-category-header {
    display: flex;
    align-items: center;
    padding: 10px 12px;
    cursor: pointer;
    background-color: #f0f2f5; /* 给文件夹一个淡淡的背景色以区分 */
    font-weight: 600; /* 加粗字体 */
}
#phone-screen.dark-mode .wb-category-header {
    background-color: #2c2c2e; /* 夜间模式下的背景色 */
}


/* 2. 展开/收起的小箭头 */
.wb-category-header .arrow {
    font-size: 12px;
    margin-right: 8px;
    transition: transform 0.2s ease;
}

/* 3. 当文件夹收起时，箭头旋转 */
.wb-category-header.collapsed .arrow {
    transform: rotate(-90deg);
}

/* 4. 存放书籍条目的容器 */
.wb-book-container {
    padding-left: 20px; /* 核心：让书籍条目向内缩进，看起来像在文件夹里 */
    max-height: 1000px; /* 一个足够大的值，用于动画 */
    overflow: hidden;
    transition: max-height 0.3s ease-in-out;
}

/* 5. 当文件夹收起时，书籍容器的高度变为0，实现动画效果 */
.wb-book-container.collapsed {
    max-height: 0;
}

/* 6. 单个书籍条目（覆盖默认的label样式，微调间距） */
.wb-book-container label {
    padding: 8px 12px;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】世界书关联选择器 - 视觉优化 ▼▼▼ */

/* 1. 让分类标题更突出 */
.wb-category-header > span:last-of-type {
    font-size: 14px;
    font-weight: 700; /* 加粗 */
    color: var(--text-primary);
}

/* 2. 为箭头设置一个漂亮的颜色循环 */
#world-book-checkboxes-container .wb-category-header:nth-of-type(6n+1) .arrow { color: #007bff; } /* 蓝色 */
#world-book-checkboxes-container .wb-category-header:nth-of-type(6n+2) .arrow { color: #28a745; } /* 绿色 */
#world-book-checkboxes-container .wb-category-header:nth-of-type(6n+3) .arrow { color: #fd7e14; } /* 橙色 */
#world-book-checkboxes-container .wb-category-header:nth-of-type(6n+4) .arrow { color: #6f42c1; } /* 紫色 */
#world-book-checkboxes-container .wb-category-header:nth-of-type(6n+5) .arrow { color: #dc3545; } /* 红色 */
#world-book-checkboxes-container .wb-category-header:nth-of-type(6n+6) .arrow { color: #ffc107; } /* 黄色 */

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* === 头像框选择模态框样式 (这是新添加的) === */
.change-frame-btn {
    padding: 6px 10px;
    border: 1px solid #ccc;
    background-color: #f0f0f0;
    border-radius: 5px;
    cursor: pointer;
    font-size: 13px;
    margin-left: 10px;
}

#avatar-frame-modal .modal-content {
    height: 70%; /* 让窗口高一点 */
}

#avatar-frame-modal .modal-body {
    padding: 0;
    display: flex;
    flex-direction: column;
}
      
.frame-tabs {
    display: flex;
    border-bottom: 1px solid var(--border-color);
    flex-shrink: 0;
}

.frame-tab {
    flex: 1;
    padding: 12px;
    text-align: center;
    font-weight: 500;
    cursor: pointer;
    color: var(--text-secondary);
    border-bottom: 2px solid transparent;
}

.frame-tab.active {
    color: var(--accent-color);
    border-bottom-color: var(--accent-color);
}

.frame-content {
    flex-grow: 1;
    overflow-y: auto;
    padding: 15px;
}

.frame-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); /* 每行自动填充，最小70px宽 */
    gap: 15px;
}

.frame-item {
    aspect-ratio: 1 / 1; /* 保持正方形 */
    border: 2px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    background-color: #f0f0f0;
    background-size: cover;
    background-position: center;
    padding: 5px;
    transition: all 0.2s ease;
    position: relative; /* 为预览图做准备 */
}

.frame-item.selected {
    border-color: var(--accent-color);
    transform: scale(1.05);
}

.frame-item .preview-avatar {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
}

.frame-item .preview-frame {
    position: absolute;
    top: -7px;
    left: 0;
    width: 100%;
    height: 100%;
}

/* ▼▼▼ 把这些CSS加回去 ▼▼▼ */
.avatar-with-frame {
    position: relative;
    width: 34px; /* 和普通头像一样大 */
    height: 34px;
    flex-shrink: 0;
}
.avatar-with-frame .avatar-img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%; /* 带框的头像通常是圆的 */
    object-fit: cover;
    z-index: 1;
}
.avatar-with-frame .avatar-frame {
    position: absolute;
    /* 核心修改1：使用百分比来定义大小 */
    /* 这会让头像框的尺寸永远是其父容器（也就是头像大小）的1.5倍 */
    /* 如果你觉得框太大了或太小了，可以微调这里的数值，比如 140% 或 160% */
    width: 135%; 
    height: 135%;

    /* 核心修改2：使用transform变换来实现完美居中 */
    top: 43%; /* 先把框的左上角移动到父容器的中心点 */
    left: 50%;
    /* 再把框向左、向上移动自己宽高的一半，这样框的中心就和父容器的中心完全对齐了 */
    transform: translate(-50%, -50%); 
    
    z-index: 2;
    pointer-events: none; /* 让框不影响鼠标事件 */
}
/* ▲▲▲ 添加结束 ▲▲▲ */

/* ▼▼▼ 【最终美化版】聊天列表左滑置顶功能样式 (已修复Bug) ▼▼▼ */

/* 1. 外层包裹容器 (已移除 display:flex) */
.chat-list-item-swipe-container {
    position: relative;
    overflow: hidden;
}

/* 2. 可滑动的内容区 (保持不变) */
.chat-list-item-content {
    position: relative;
    z-index: 2;
    background-color: var(--secondary-bg);
    transition: transform 0.3s ease, background-color 0.3s ease;
    width: 100%;
    flex-shrink: 0;
}

/* 3. 【核心修改1】置顶聊天时，使用对比度更强的背景色 */
.chat-list-item-content.pinned {
    background-color: #f0f2f5; /* 日间模式下的置顶颜色 (已加深) */
}
#phone-screen.dark-mode .chat-list-item-content.pinned {
    background-color: #3a3a3c; /* 夜间模式下的置顶颜色 (已加深) */
}

/* 4. 隐藏的操作按钮区域 (保持不变) */
.swipe-actions {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    display: flex;
}

/* 5. 单个操作按钮的样式 (保持不变) */
.swipe-action-btn {
    height: 100%;
    padding: 0 20px;
    border: none;
    color: white;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 80px;
}

.swipe-action-btn.pin { background-color: #f0ad4e; }
.swipe-action-btn.unpin { background-color: #777; }
.swipe-action-btn.delete { background-color: #ff3b30; }

/* 6. 当内容区被滑开时 (保持不变) */
.chat-list-item-content.swiped {
    transform: translateX(-160px);
}

/* 7. 分割线的正确逻辑 (保持不变) */
.chat-list-item {
    border-bottom: none;
}
.chat-list-item-swipe-container:not(:last-child) {
     border-bottom: 1px solid var(--border-color);
}
.chat-group-container {
    border-bottom: 1px solid var(--border-color);
}
.chat-group-container:first-of-type {
    border-top: 1px solid var(--border-color);
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 用下面这段【新样式】，替换掉你旧的 .date-stamp 样式 ▼▼▼ */

/* 日期戳的外层包裹，让它像系统消息一样居中 */
.date-stamp-wrapper {
    justify-content: center;
    align-self: center;
    margin: 10px 0;
    max-width: 80%;
}

/* 日期戳的气泡本身 */
.date-stamp-bubble {
    background-color: rgba(0, 0, 0, 0.1);
    color: var(--text-secondary);
    font-size: 12px;
    padding: 4px 12px;
    border-radius: 10px;
}

/* 适配夜间模式 */
#phone-screen.dark-mode .date-stamp-bubble {
    background-color: rgba(255, 255, 255, 0.15);
}
/* ▲▲▲ 替换结束 ▲▲▲ */

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */
.chat-list-time {
    font-size: 12px;
    color: var(--text-secondary);
    text-align: right;
    margin-left: 8px; /* 和中间的信息区拉开一点距离 */
    flex-shrink: 0;   /* 防止在空间不足时被压缩 */
    align-self: flex-start; /* 让它和顶部的名字对齐 */
    padding-top: 2px; /* 微调垂直位置 */
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 请将这段新CSS粘贴到 <style> 的末尾 ▼▼▼ */
.chat-list-right-column {
    display: flex;
    flex-direction: column;
    align-items: flex-end; /* 让内容靠右对齐 */
    gap: 4px; /* 在时间和红点之间加一点点间距 */
    flex-shrink: 0;
    margin-left: 8px;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】聊天记录搜索功能样式 ▼▼▼ */

/* 搜索结果条目样式 */
.search-result-item {
    padding: 12px 15px;
    border-bottom: 1px solid var(--border-color);
    cursor: pointer;
    display: flex;
    gap: 12px;
    align-items: flex-start;
}
.search-result-item:hover {
    background-color: #f5f5f5;
}
#phone-screen.dark-mode .search-result-item:hover {
    background-color: #2c2c2e;
}

.search-result-item .avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.search-result-item .result-content {
    flex-grow: 1;
    overflow: hidden;
}

.search-result-item .result-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 4px;
}

.search-result-item .result-sender {
    font-weight: 600;
    font-size: 15px;
}

.search-result-item .result-timestamp {
    font-size: 12px;
    color: var(--text-secondary);
}

.search-result-item .result-message-snippet {
    font-size: 14px;
    color: var(--text-primary);
    line-height: 1.5;
    word-break: break-word;
}

/* 搜索结果中的高亮关键词 */
.search-result-item .highlight {
    background-color: #ffd700;
    color: #000;
    font-weight: bold;
    border-radius: 3px;
    padding: 1px 3px;
}
#phone-screen.dark-mode .search-result-item .highlight {
    background-color: #ffc107;
}


/* ▼▼▼ 【全新】聊天记录高亮功能样式 (文字高亮版) ▼▼▼ */

/* 1. 定义一个新的动画，这次我们同时改变背景色和文字颜色 */
@keyframes jump-highlight-text {
    0% {
        background-color: transparent; /* 动画开始时，文字背景是透明的 */
        color: inherit; /* 文字颜色继承它本来的颜色 */
    }
    50% {
        background-color: #ffd700; /* 在动画中间，文字背景变为高亮的黄色 */
        color: #000000 !important; /* 同时，强制把文字颜色变为黑色，确保在黄底上清晰可见 */
    }
    100% {
        background-color: transparent; /* 动画结束，文字背景恢复透明 */
        color: inherit; /* 文字颜色也恢复原样 */
    }
}

/* 2. 将这个新动画应用到消息气泡的 .content 区域 */
/*    这样它就只会影响文字区域，而不会影响整个气泡 */
.jump-highlight-animation .content {
    animation: jump-highlight-text 1.5s ease-in-out;
    border-radius: 5px; /* (可选)给高亮区域一点点圆角，更好看 */
}

/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】搜索界面美化样式 ▼▼▼ */
#chat-search-screen {
    background-color: #f0f2f5; /* 和其他内容页统一的背景色 */
}
#search-controls {
    background-color: var(--secondary-bg); /* 让控制区背景能适配夜间模式 */
}
#phone-screen.dark-mode #chat-search-screen {
    background-color: #000000;
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】锁屏与密码界面样式 ▼▼▼ */

/* 1. 锁屏界面的总容器 */
#lock-screen {
    /* 核心：用一个非常高的 z-index 确保它能覆盖所有其他屏幕 */
    z-index: 999; 
    background-size: cover;
    background-position: center;
    color: white;
    display: flex;
    flex-direction: column;
    justify-content: space-between; /* 让时钟和提示语上下分布 */
    align-items: center;
    padding: 80px 20px 50px 20px;
    box-sizing: border-box;
    transition: transform 0.3s ease-out;
}

/* 2. 锁屏时钟 (复用主屏幕时钟的样式) */
#lock-clock-container {
    text-align: center;
    text-shadow: 0 3px 8px rgba(0,0,0,0.4);
    flex-shrink: 0;
}
#lock-main-time {
    font-size: 80px;
    font-weight: 200;
}
#lock-main-date {
    font-size: 18px;
    font-weight: 500;
}

/* 3. “向上轻扫”的提示文字和动画 */
#unlock-hint {
    font-size: 16px;
    font-weight: 500;
    text-shadow: 0 1px 4px rgba(0,0,0,0.3);
    /* 添加一个呼吸动画，吸引用户注意 */
    animation: hint-pulse 2s infinite ease-in-out;
}
@keyframes hint-pulse {
    0%, 100% { opacity: 0.7; transform: translateY(0); }
    50% { opacity: 1; transform: translateY(-5px); }
}

/* 4. 密码输入弹窗的遮罩层 */
#password-modal-overlay {
    /* 使用一个半透明的毛玻璃效果背景 */
    background-color: rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    z-index: 1000; /* 比锁屏更高，确保在最上层 */
}

/* 5. 密码输入弹窗的内容区 */
.password-modal-content {
    background-color: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    width: 280px;
    border-radius: 15px;
    padding: 20px;
    box-shadow: 0 5px 20px rgba(0,0,0,0.2);
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 15px;
    color: #333;
}
.password-modal-content p {
    font-size: 17px;
    font-weight: 600;
    margin: 0;
}

/* 6. 密码输入框 */
#password-input-field {
    width: 100%;
    padding: 12px;
    border: 1px solid rgba(0,0,0,0.1);
    border-radius: 8px;
    font-size: 16px;
    text-align: center;
    box-sizing: border-box;
    background-color: rgba(255,255,255,0.7);
}
#password-input-field:focus {
    outline: none;
    border-color: var(--accent-color);
}

/* 7. 密码弹窗的按钮区域 */
.password-actions {
    display: flex;
    gap: 10px;
    margin-top: 5px;
}
.password-actions button {
    flex: 1;
    padding: 12px;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    font-weight: 500;
    cursor: pointer;
}
#password-cancel-btn {
    background-color: #e9ecef;
    color: #495057;
}
#password-confirm-btn {
    background-color: var(--accent-color);
    color: white;
}

/* 8. 密码错误时的晃动动画 */
@keyframes shake-error {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
  20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.password-modal-content.error {
    animation: shake-error 0.4s ease-in-out;
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】气泡/API样式预设功能CSS ▼▼▼ */
.bubble-preset-manager {
    display: flex;
    align-items: center;
    gap: 10px; /* 元素之间的间距 */
}

/* 下拉框的样式，让它占据大部分空间 */
.bubble-preset-manager select {
    flex-grow: 1; /* 占据所有剩余空间 */
}

/* 管理按钮的样式，让它小巧精致 */
.bubble-preset-manager .action-btn {
    flex-shrink: 0; /* 防止按钮被压缩 */
    padding: 8px 10px;
    font-size: 13px;
    background-color: #e9ecef;
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-radius: 5px;
    cursor: pointer;
    font-weight: 500;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 把这一整块全新的CSS，粘贴到 <style> 的末尾 ▼▼▼ */

/* 1. 让锁屏和主屏幕可以堆叠 */
/* 核心：把锁屏和主屏幕都设为绝对定位，这样它们才能重叠 */
#lock-screen, #home-screen {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

/* 2. 定义毛玻璃背景层的样式 */
#lock-screen-background-blur {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1; /* 【核心修改1】它的层级现在和主屏幕一样低 */
    
    background-size: cover;
    background-position: center;

    /* 【核心修改2】魔法在这里！我们直接模糊这个元素本身 */
    filter: blur(20px);
    -webkit-filter: blur(20px);
    
    /* (可选但推荐) 轻微放大可以避免模糊后边缘变暗，效果更好 */
    transform: scale(1.1); 

    /* 【核心修改3】默认隐藏，且带有淡入淡出效果 */
    display: none;
    opacity: 0;
    transition: opacity 0.3s ease-out;
}

/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 从这里开始复制 ▼▼▼ */

/* === 【终极修复版】世界书选择器列表样式 === */

/* 1. 分类文件夹行样式：用gap创建固定间距，超稳定 */
.wb-category-header {
    display: flex;
    align-items: center;
    gap: 8px; /* 箭头、复选框、文字之间的固定间距 */
    padding: 10px 12px;
    cursor: pointer;
    background-color: #f0f2f5;
    font-weight: 600;
    overflow: hidden; /* 防止任何意外 */
}

/* 2. 分类文件夹里的文字样式：只负责截断，不负责布局 */
.wb-category-header > span:last-of-type {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 3. 【核心修复】为分类文件夹和书目里的复选框“解绑”全局样式！ */
.wb-category-header input[type="checkbox"],
.wb-book-container input[type="checkbox"] {
    width: auto !important; /* 让它恢复自己的天然大小，!important确保最高优先级 */
    flex-shrink: 0;         /* 防止它被压缩 */
}

/* 4. 具体书目行的样式：强制从左边开始排列，解决iOS问题 */
.wb-book-container label {
    display: flex;
    align-items: center;
    justify-content: flex-start; /* 强制所有内容从左边开始！*/
    padding: 8px 12px;
    gap: 10px; /* 复选框和文字的间距 */
    overflow: hidden;
}

/* 5. 具体书目的文字样式 */
.wb-book-container label .wb-book-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
}

/* 6. 夜间模式适配 */
#phone-screen.dark-mode .wb-category-header {
    background-color: #2c2c2e;
}

/* 7. 小箭头的样式（保持不变） */
.wb-category-header .arrow {
    font-size: 12px;
    transition: transform 0.2s ease;
}
.wb-category-header.collapsed .arrow {
    transform: rotate(-90deg);
}

/* 8. 书籍容器的样式（保持不变） */
.wb-book-container {
    padding-left: 20px;
    max-height: 1000px;
    overflow: hidden;
    transition: max-height 0.3s ease-in-out;
}
.wb-book-container.collapsed {
    max-height: 0;
}

/* ▲▲▲ 复制到这里结束 ▲▲▲ */

/* ▼▼▼ 【全新】鼠标拖动工具栏时的手势样式 ▼▼▼ */
#chat-input-actions-top.grabbing {
    cursor: grabbing;
    cursor: -webkit-grabbing;
    user-select: none; /* 防止拖动时选中文本 */
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【SVG终极版】发送定位功能样式 ▼▼▼ */

/* 1. 基础卡片和气泡样式 (保持不变) */
.message-bubble.is-location .content { padding: 0; background: transparent; box-shadow: none; border: none; backdrop-filter: none; -webkit-backdrop-filter: none; }
.location-card { width: 230px; background-color: #f7f7f7; border-radius: 12px; overflow: hidden; border: 1px solid #e0e0e0; box-shadow: 0 2px 5px rgba(0,0,0,0.08); }

/* 2. 【核心升级】地图区域现在直接用于容纳SVG */
.location-map-area {
    height: 90px;
    background-color: #f0f2f5;
    background-image:
        linear-gradient(rgba(0,0,0,0.05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0,0,0,0.05) 1px, transparent 1px);
    background-size: 20px 20px;
    padding: 5px; /* 给SVG一点呼吸空间 */
    box-sizing: border-box;
}
.location-map-area svg {
    width: 100%;
    height: 100%;
}

/* 3. 【全新】SVG内部元素的样式 */
/* 轨迹曲线 */
.svg-trajectory-path {
    stroke-width: 2px;
    stroke: rgba(180, 180, 180, 0.8);
    stroke-dasharray: 3 3; /* 虚线效果 */
    fill: none;
}
/* 定位点 (起点/终点) */
.svg-pin {
    stroke-width: 2px;
    stroke: white;
}
.svg-pin.user-pin { fill: #4CAF50; } /* 绿色用户点 */
.svg-pin.ai-pin { fill: #ff5252; } /* 红色AI点 */

/* 脚印图标 */
.svg-footprint {
    font-size: 14px;
    fill: rgba(0, 0, 0, 0.4);
}
/* 途经点地名标签 */
.svg-location-label {
    font-size: 10px;
    font-weight: 500;
    fill: #555;
    /* 给文字一点白色的描边，让它在复杂背景下更清晰 */
    stroke: white;
    stroke-width: 2px;
    paint-order: stroke;
}

/* 4. 卡片底部信息区域 (已升级) */
.location-info { padding: 12px; }
.location-address {
    font-size: 13px;
    line-height: 1.6;
    margin-bottom: 8px;
    color: #333;
}
.location-address .name-tag {
    font-weight: 600;
    color: var(--text-primary);
}
.location-address p.hidden { display: none; }
.location-distance {
    font-size: 12px;
    color: var(--text-secondary);
    border-top: 1px solid #e9e9e9;
    padding-top: 8px;
    text-align: center;
}
/* ▲▲▲ 升级版CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】“一键重roll”按钮样式 ▼▼▼ */
#reroll-btn {
    position: static;
    width: 40px;
    height: 40px;
    padding: 0;
    border-radius: 20px;
    background-color: rgba(255, 255, 255, 0.6);
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
    border: 1px solid rgba(0,0,0,0.08);
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.2s, transform 0.1s;
    cursor: pointer;
    color: var(--text-primary); /* 使用全局主题的主文本颜色 */
}
#reroll-btn:hover {
    opacity: 0.8;
}
#reroll-btn:active {
    transform: scale(0.9);
}
#reroll-btn svg {
    width: 20px;
    height: 20px;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【最终美化版】悬浮歌词栏样式 ▼▼▼ */
#floating-lyrics-bar {
    position: absolute;
    /* 【问题1修复】默认位置改为顶部 */
    top: 50px; 
    left: 50%;
    transform: translateX(-50%);
    z-index: 100;
    
    display: none;
    align-items: center;
    justify-content: center;
    gap: 10px; /* 在歌词和按钮间增加间距 */

    width: 90%; 
    max-width: 320px; /* 同时设一个最大像素宽度，避免在宽屏上过长 */
    padding: 8px 12px; /* 稍微增加一点内边距以适应新宽度 */
    background-color: rgba(0, 0, 0, 0); 
    
    /* 【问题3需要】为字体颜色和背景透明度添加过渡动画 */
    transition: background-color 0.3s, opacity 0.3s;

    color: white;
    font-size: 14px;
    font-weight: 500; /* 字体稍微加粗 */
    text-align: center;
    border-radius: 12px;
    
    /* 文字阴影，让它在任何背景下都清晰 */
    text-shadow: 0 1px 3px rgba(0,0,0,0.5); 
    
    cursor: pointer;
    user-select: none;
    
    /* 【问题4需要】为关闭按钮准备 */
    overflow: visible; /* 允许按钮超出范围 */
}

/* 【问题4需要】关闭按钮的样式 */
#floating-lyrics-bar .close-btn,
#floating-lyrics-bar #lyrics-settings-btn {
    position: absolute;
    top: -8px;
    width: 20px;
    height: 20px;
    background-color: rgba(0,0,0,0.6);
    color: white;
    border-radius: 50%;
    border: 1px solid white;
    cursor: pointer;
    opacity: 0; /* 默认隐藏 */
    transition: opacity 0.2s;
    display: flex; /* 确保SVG能居中 */
    align-items: center;
    justify-content: center;
}

/* 把关闭按钮和设置按钮分开放置 */
#floating-lyrics-bar .close-btn { right: -8px; line-height: 18px; font-size: 14px; }
#floating-lyrics-bar #lyrics-settings-btn { right: 22px; } /* 放在关闭按钮旁边 */

/* 鼠标悬停在整个歌词栏上时，一起显示它们 */
#floating-lyrics-bar:hover .close-btn,
#floating-lyrics-bar:hover #lyrics-settings-btn {
    opacity: 1;
}

#floating-lyrics-bar.dragging {
    cursor: grabbing;
    cursor: -webkit-grabbing;
}
/* ▲▲▲ 新CSS粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】主屏幕滑动功能样式 ▼▼▼ */

/* 修正 #home-screen 的flex布局，以适应新的滑动容器 */
#home-screen {
    justify-content: flex-start; /* 内容从顶部开始，而不是居中 */
    padding-bottom: 20px; /* 给小圆点留出空间 */
}

/* 滑动轨道容器 */
.home-screen-slider {
    display: flex;
    height: 100%;
    transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

/* 每一页 */
.home-screen-page {
    flex: 0 0 100%; /* 每页固定100%屏宽 */
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    padding: 20px 0;
    box-sizing: border-box;
}

/* 分页小圆点的容器 */
.pagination-dots {
    position: absolute;
    bottom: 25px; /* 定位在屏幕底部 */
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 8px;
}

/* 单个小圆点的样式 */
.dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.4);
    transition: background-color 0.3s;
}

/* 当前激活页面的小圆点样式 */
.dot.active {
    background-color: rgba(255, 255, 255, 0.9);
}

/* ▲▲▲ 新样式结束 ▲▲▲ */

/* ▼▼▼ 【全新】第二页主屏幕图标网格样式 ▼▼▼ */
.app-grid-standard {
    display: grid; /* 使用现代的网格布局，而不是flex */
    grid-template-columns: repeat(4, 1fr); /* 创建一个4列的网格，每列宽度平分 */
    gap: 20px; /* 图标之间的上下左右间距 */
    padding: 20px 20px; /* 在网格的上下和左右各留出一点边距 */
    margin-top: 0; /* 覆盖掉原有的 margin-top: auto，让图标从顶部开始排列 */
    width: 75%;
    box-sizing: border-box; /* 确保 padding 不会撑破容器 */
}

/* 覆盖掉 .app-grid 中可能影响布局的旧样式 */
.app-grid-standard {
    align-items: flex-start; /* 从顶部对齐 */
    justify-content: flex-start; /* 从左侧对齐 */
}
/* ▲▲▲ 新样式粘贴结束 ▲▲▲ */

/* ▼▼▼ 【全新】“查角色手机”功能相关样式 ▼▼▼ */

/* 角色选择列表项 */
.character-select-item {
    display: flex;
    align-items: center;
    padding: 10px 15px;
    border-bottom: 1px solid var(--border-color);
    cursor: pointer;
}
.character-select-item:hover {
    background-color: #f5f5f5;
}
.character-select-item img {
    width: 45px;
    height: 45px;
    border-radius: 50%;
    margin-right: 12px;
}
.character-select-item .name {
    font-weight: 500;
}

/* 角色手机的聊天气泡 (简化版) */
.character-chat-bubble {
    padding: 8px 12px;
    border-radius: 12px;
    max-width: 80%;
    word-break: break-word;
    line-height: 1.5;
}
.character-chat-bubble.sent {
    background-color: #dcf8c6;
    align-self: flex-end; /* 自己发的靠右 */
}
.character-chat-bubble.received {
    background-color: #ffffff;
    align-self: flex-start; /* 收到的靠左 */
}

/* ▼▼▼ 【美化版】角色手机数据列表项样式 ▼▼▼ */
.character-data-item {
    padding: 12px 15px;
    border-bottom: 1px solid #f0f0f0; /* 边框颜色变浅 */
    background-color: #ffffff; /* 增加白色背景 */
    margin: 8px 10px; /* 增加外边距，形成卡片感 */
    border-radius: 8px; /* 增加圆角 */
    box-shadow: 0 1px 3px rgba(0,0,0,0.05); /* 增加非常淡的阴影 */
    transition: transform 0.2s, box-shadow 0.2s; /* 增加悬浮动画 */
}

.character-data-item:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.08);
}

.character-data-item .title {
    font-weight: 600;
    margin-bottom: 5px;
    color: #333; /* 标题颜色加深 */
}
.character-data-item .content {
    font-size: 14px;
    color: #555;
    line-height: 1.6;
    white-space: pre-wrap;
}
.character-data-item .meta {
    font-size: 12px;
    color: #888;
    margin-top: 10px;
    padding-top: 8px; /* 在meta上方增加一点距离和分割线 */
    border-top: 1px solid #f5f5f5;
    display: flex;
    justify-content: space-between;
}
/* ▲▲▲ 替换结束 ▲▲▲ */

/* ▼▼▼ 【V3修正版】角色手机“画中画”样式修正 ▼▼▼ */

/* 1. 【核心修正1】让手机外壳容器占据整个屏幕，并用内边距把手机框“推”下来 */
#character-phone-container {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #f0f2f5;
    padding-top: 50px; /* ★★★ 在顶部留出50px空间，把手机框推到主顶栏下方 ★★★ */
    box-sizing: border-box; /* 确保内边距计算正确 */
}

/* 2. 手机边框样式 (保持不变) */
.character-phone-frame {
    width: 95%;
    height: 98%;
    background-color: #1a1a1a;
    border-radius: 30px;
    padding: 10px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.2);
    box-sizing: border-box;
    position: relative;
    display: flex;
}

/* 3. 【核心修改1】缩小App图标的背景方格 */
#character-app-grid .app-icon .icon-bg {
    width: 55px;  /* ★★★ 从 65px 显著缩小到 50px ★★★ */
    height: 55px; /* ★★★ 高度同步缩小 ★★★ */
    border-radius: 12px; /* 圆角也可以稍微小一点 */
}

/* 3. 【核心修正2】缩小APP图标内的SVG */
#character-app-grid .app-icon .icon-bg svg {
    width: 60%;  /* ★★★ 将SVG的宽度缩小到其容器的60% ★★★ */
    height: 60%; /* ★★★ 高度也同步缩小 ★★★ */
}

/* 4. 其余样式保持不变 */
.character-phone-inner-screen {
    flex-grow: 1;
    background-color: #fff;
    border-radius: 20px;
    overflow: hidden;
    position: relative;
}
.character-phone-notch {
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    width: 100px;
    height: 20px;
    background-color: #1a1a1a;
    border-radius: 0 0 10px 10px;
    z-index: 10;
}
.character-phone-page {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s, visibility 0.3s;
}
.character-phone-page.active {
    opacity: 1;
    visibility: visible;
    z-index: 5;
}
.character-phone-header {
    flex-shrink: 0;
    padding: 8px 12px;
    padding-top: 35px;
    background-color: rgba(247, 247, 247, 0.7);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    font-size: 16px;
}
.character-phone-header .action-btn svg,
.character-phone-header .back-btn svg {
    color: var(--accent-color);
}
#character-chat-history-messages {
    background-image: none !important;      /* ★★★ 移除背景图片 ★★★ */
    background-color: #F5F5F5
 !important; /* ★★★ 设置为白色背景 ★★★ */
}
/* ▲▲▲ 样式修正结束 ▲▲▲ */

/* ▼▼▼ 【V5修正版】角色手机APP图标布局修正 ▼▼▼ */
#character-app-grid.app-grid-standard {
    grid-template-columns: repeat(3, 1fr); /* 核心：从4列改为3列 */
    width: 100%; /* 稍微加宽一点，让3个图标的间距更和谐 */
    padding-top: 70px; /* 稍微增加顶部距离，避免和刘海太近 */
}
/* ▲▲▲ 样式修正结束 ▲▲▲ */

/* ▼▼▼ 【全新】角色手机微信风格聊天气泡样式 ▼▼▼ */

/* 1. 聊天记录界面的背景色 */
#character-chat-history-messages {
    background-color: #ededed; /* 微信经典的灰色背景 */
    /* 移除之前版本可能存在的背景图片 */
    background-image: none;
}

/* 2. 消息气泡的通用容器样式 */
.character-chat-bubble-container {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    margin-bottom: 12px;
    max-width: 80%; /* 限制气泡最大宽度 */
}

/* 3. 气泡的内容区域 */
.character-chat-bubble {
    position: relative;
    padding: 8px 12px;
    line-height: 1.5;
    word-break: break-word;
    border-radius: 6px;
    font-size: 15px;
}

/* 4. 头像样式 */
.character-chat-avatar {
    width: 36px;
    height: 36px;
    border-radius: 6px; /* 微信风格的圆角矩形头像 */
    flex-shrink: 0;
}

/* --- 我方（角色自己）的气泡 --- */
.character-chat-bubble-container.sent {
    align-self: flex-end; /* 整体靠右 */
    flex-direction: row-reverse; /* 布局反转：内容 -> 头像 */
}
.character-chat-bubble-container.sent .character-chat-bubble {
    background-color: #95ec69; /* 微信绿 */
    color: #000;
}
/* 我方气泡的小尾巴 */
.character-chat-bubble-container.sent .character-chat-bubble::after {
    content: "";
    position: absolute;
    right: -4px;
    top: 10px;
    width: 0;
    height: 0;
    border: 4px solid transparent;
    border-left-color: #95ec69;
    border-right: 0;
}

/* --- 对方（NPC或用户）的气泡 --- */
.character-chat-bubble-container.received {
    align-self: flex-start; /* 整体靠左 */
}
.character-chat-bubble-container.received .character-chat-bubble {
    background-color: #ffffff; /* 白色 */
    color: #000;
}
/* 对方气泡的小尾巴 */
.character-chat-bubble-container.received .character-chat-bubble::before {
    content: "";
    position: absolute;
    left: -4px;
    top: 10px;
    width: 0;
    height: 0;
    border: 4px solid transparent;
    border-right-color: #ffffff;
    border-left: 0;
}

/* --- 功能性重置：针对图片/表情包，让气泡“消失” --- */
.character-chat-bubble-container .character-chat-bubble:has(img.sticker-image) {
    background: transparent !important;
    padding: 0 !important;
    border-radius: 0;
}
.character-chat-bubble-container .character-chat-bubble:has(img.sticker-image)::before,
.character-chat-bubble-container .character-chat-bubble:has(img.sticker-image)::after {
    content: none !important;
}
/* ▲▲▲ 样式添加结束 ▲▲▲ */

/* ▼▼▼ 【全新】角色手机新增APP页面样式 ▼▼▼ */

/* 1. 相册页面的网格布局 */
#character-album-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr); /* 每行3张图 */
    gap: 4px;
    padding: 4px;
}
.character-album-item {
    aspect-ratio: 1 / 1; /* 保持正方形 */
    background-color: #e0e0e0;
    cursor: pointer;
}
.character-album-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* 2. 银行页面的余额显示 */
.character-bank-balance-card {
    margin: 15px;
    padding: 20px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    border-radius: 12px;
    text-align: center;
    box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.character-bank-balance-card .label {
    font-size: 14px;
    opacity: 0.8;
}
.character-bank-balance-card .amount {
    font-size: 32px;
    font-weight: 600;
    margin-top: 5px;
}

/* 3. 银行交易记录的颜色 */
.character-data-item .transaction-amount {
    font-weight: 600;
}
.character-data-item .transaction-amount.income {
    color: #4CAF50; /* 收入为绿色 */
}
.character-data-item .transaction-amount.expense {
    color: #F44336; /* 支出为红色 */
}

/* ▲▲▲ 样式添加结束 ▲▲▲ */

/* ▼▼▼ 【V5修正版】角色手机相册网格布局 ▼▼▼ */
#character-album-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr); /* 每行3张图 */
    gap: 4px;
    padding: 4px;
}
.character-album-item {
    aspect-ratio: 1 / 1; /* 保持正方形 */
    background-color: #e0e0e0;
    cursor: pointer;
}
.character-album-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* ▲▲▲ 样式修正结束 ▲▲▲ */

/* ▼▼▼ 【全新】Markdown渲染增强样式 ▼▼▼ */

/* 1. 为角色手机里的所有Markdown内容设置基础样式 */
.character-data-item .content h1,
.character-data-item .content h2,
.character-data-item .content h3,
.character-data-item .content p {
    margin: 0 0 10px 0; /* 统一标题和段落的下边距 */
}
.character-data-item .content h1 { font-size: 1.5em; font-weight: 600; }
.character-data-item .content h2 { font-size: 1.3em; font-weight: 600; }
.character-data-item .content h3 { font-size: 1.1em; font-weight: 600; }

/* 2. 删除线样式 */
.character-data-item .content del {
    color: #8a8a8a;
}

/* 3. 遮挡/剧透效果 */
.character-data-item .content .spoiler {
    background-color: #333;
    color: #333; /* 文字和背景一个色，实现隐藏 */
    padding: 0 4px;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
}
/* 点击或鼠标悬停时显示内容 */
.character-data-item .content .spoiler:hover,
.character-data-item .content .spoiler:active {
    background-color: #f0f0f0;
    color: #000;
}

/* ▲▲▲ 样式添加结束 ▲▲▲ */

/* ▼▼▼ 【全新】角色手机日记美化与删除功能样式 ▼▼▼ */

/* 1. 让日记列表有更好的边距 */
#character-diary-list {
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 12px; /* 增加日记之间的间距 */
}

/* 2. 美化单篇日记卡片 */
#character-diary-list .character-data-item {
    background-color: #fffaf0; /* 温暖的米黄色背景 */
    border-left: 4px solid #ffc107; /* 左侧加一条装饰线 */
    box-shadow: 0 2px 5px rgba(0,0,0,0.05);
    position: relative; /* 为了定位删除按钮 */
    padding-bottom: 35px; /* 为底部的日期留出空间 */
}

/* 3. 美化日期显示，把它放到右下角 */
#character-diary-list .character-data-item .meta {
    position: absolute;
    bottom: 8px;
    right: 12px;
    border-top: none; /* 移除原来的上边框 */
    padding-top: 0;
    font-size: 11px;
    color: #bfa87a;
}

/* 4. 删除按钮的样式 */
.diary-delete-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background-color: rgba(0,0,0,0.05);
    color: #bfa87a;
    border: none;
    cursor: pointer;
    font-size: 20px;
    line-height: 26px;
    text-align: center;
    opacity: 0; /* 默认隐藏 */
    transition: opacity 0.2s ease-in-out;
}
/* 鼠标悬停在日记上时显示按钮 */
#character-diary-list .character-data-item:hover .diary-delete-btn {
    opacity: 1;
}
.diary-delete-btn:hover {
    background-color: #ff3b30;
    color: white;
}
/* ▲▲▲ 样式添加结束 ▲▲▲ */

/* ▼▼▼ 【V6最终修复版】角色手机相册布局防溢出 ▼▼▼ */

/* 1. 【核心】为相册网格的容器强制禁止水平滚动 */
#character-album-grid.list-container {
    overflow-x: hidden;
}

/* 2. 重新定义网格布局 */
#character-album-grid {
    display: grid;
    /* 【核心】每行3个，但这次我们用calc()精确计算宽度 */
    grid-template-columns: repeat(3, calc(33.333% - 4px)); 
    gap: 6px; /* 稍微增大间隙，让calc有计算空间 */
    padding: 6px; /* 内边距和间隙保持一致 */
    box-sizing: border-box; /* 确保内边距和边框被包含在总宽度内 */
}

/* 3. 稍微缩小图片，确保它们不会撑破容器 */
.character-album-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block; /* 移除图片下方的微小空隙 */
}

/* ▲▲▲ 样式修正结束 ▲▲▲ */

/* ▼▼▼ 【V6独家定制】角色手机信纸风格日记样式 ▼▼▼ */

/* 1. 【核心】重塑日记卡片，让它像一张信纸 */
#character-diary-list .character-data-item {
    background-color: #fdfaf2; /* 温暖、柔和的米白/浅黄色，模拟信纸 */
    border: 1px solid #eaddc7;  /* 淡淡的纸张边缘色 */
    border-left: 3px solid #d4bda5; /* 左侧加一条稍深的线，像装订线 */
    box-shadow: 2px 2px 6px rgba(0,0,0,0.06); /* 更柔和的阴影 */
    position: relative;
    padding: 20px 15px 15px 20px; /* 调整内边距，给“小东西”留出空间 */
    font-family: Georgia, 'Times New Roman', 'Kaiti TC', 'STKaiti', serif; /* 使用更典雅的衬线字体 */
}

/* 2. 【小东西】用伪元素在左上角添加一个精致的纸夹 */
#character-diary-list .character-data-item::before {
    content: '📎'; /* 这是一个Emoji纸夹，简单又有效 */
    position: absolute;
    top: -12px;
    left: 15px;
    font-size: 24px;
    transform: rotate(-25deg); /* 让纸夹有一个随意的角度 */
    opacity: 0.8;
}

/* 3. 【核心】重置Markdown内容的字体，确保它们继承信纸的字体 */
#character-diary-list .character-data-item .content,
#character-diary-list .character-data-item .content h1,
#character-diary-list .character-data-item .content h2,
#character-diary-list .character-data-item .content h3 {
    font-family: inherit; /* 强制继承父元素的字体 */
    color: #4a443b; /* 使用深棕色文字，更有质感 */
}

#character-diary-list .character-data-item .content p {
    margin: 0 0 12px 0;
}

/* 4. 将日期移动到右上角，像信纸的落款日期 */
#character-diary-list .character-data-item .meta {
    position: absolute;
    top: 10px;
    right: 12px;
    font-size: 11px;
    color: #ae9c82; /* 匹配信纸风格的日期颜色 */
    font-style: italic; /* 斜体更有手写感 */
    border-top: none;
    padding-top: 0;
}

/* 5. 美化删除按钮，让它更融入信纸风格 */
#character-diary-list .character-data-item .diary-delete-btn {
    background-color: transparent;
    color: #c9bbae;
    font-size: 22px;
    transition: all 0.2s ease;
}

#character-diary-list .character-data-item .diary-delete-btn:hover {
    background-color: #e44d44;
    color: white;
    transform: scale(1.1);
}

/* ▲▲▲ 样式添加结束 ▲▲▲ */

/* ▼▼▼ 【全新】角色手机全APP美化样式 ▼▼▼ */

/* --- 1. 购物车样式 --- */
.character-cart-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 15px;
    border-bottom: 1px solid #f0f0f0;
}
.character-cart-item:last-child {
    border-bottom: none;
}
.cart-item-icon {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    background-color: #f0f2f5;
    border-radius: 8px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: var(--text-secondary);
}
.cart-item-info {
    flex-grow: 1;
}
.cart-item-info .title {
    font-weight: 500;
}
.cart-item-info .store {
    font-size: 12px;
    color: var(--text-secondary);
}
.cart-item-price {
    font-weight: 600;
    font-size: 15px;
}

/* --- 2. 浏览器历史样式 --- */
.character-browser-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 15px;
    border-bottom: 1px solid #f0f0f0;
    cursor: pointer;
    transition: background-color 0.2s;
}
.character-browser-item:hover {
    background-color: #f9f9f9;
}
.browser-item-icon {
    font-size: 20px;
    color: var(--text-secondary);
}

/* --- 3. 银行交易明细样式 --- */
.character-bank-transaction {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 15px;
    border-bottom: 1px solid #f0f0f0;
}
.transaction-details {
    display: flex;
    align-items: center;
    gap: 12px;
}
.transaction-icon {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 18px;
    color: white;
}

/* --- 4. 行动轨迹 (时间线) 样式 --- */
.character-trajectory-list {
    padding: 20px 15px 20px 30px; /* 左侧留出时间线的空间 */
}
.character-trajectory-item {
    position: relative;
    padding-bottom: 25px;
}
/* 时间轴的竖线 */
.character-trajectory-item::before {
    content: '';
    position: absolute;
    top: 5px;
    left: -18px;
    width: 2px;
    height: 100%;
    background-color: #e0e0e0;
}
.character-trajectory-item:last-child::before {
    display: none; /* 最后一个条目没有线 */
}
/* 时间轴的圆点 */
.character-trajectory-item::after {
    content: '';
    position: absolute;
    top: 5px;
    left: -23px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background-color: var(--accent-color);
    border: 2px solid white;
    box-shadow: 0 0 0 2px var(--accent-color);
}
.trajectory-item-content .meta {
    margin-top: 4px; /* 让时间和地点离标题近一点 */
}

/* --- 5. APP使用记录 (进度条) 样式 --- */
.character-app-usage-item {
    padding: 15px;
    border-bottom: 1px solid #f0f0f0;
}
.app-usage-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;
    font-size: 14px;
}
.app-usage-header .name {
    font-weight: 500;
}
.app-usage-header .duration {
    color: var(--text-secondary);
}
.app-usage-bar-container {
    width: 100%;
    height: 6px;
    background-color: #e9ecef;
    border-radius: 3px;
    overflow: hidden;
}
.app-usage-bar {
    height: 100%;
    background-color: var(--accent-color);
    border-radius: 3px;
    transition: width 0.5s ease-in-out;
}

/* ▲▲▲ 样式添加结束 ▲▲▲ */

#status-bar-time::after {
    content: "JCY";
    color: var(--status-bar-text-color); /* <-- 应用变量 */
    font-size: 11px;
    font-weight: 500;
    opacity: 0.6; /* 统一的不透明度 */
    margin-left: 8px;
    user-select: none;
    transition: color 0.3s;
}

    </style>
    <!-- ▼▼▼ 在这里添加下面这行 ▼▼▼ -->
    <style id="custom-theme-style"></style>
</head>
<body>
        <div id="phone-screen">
            <div id="status-bar">
                <span id="status-bar-time">12:00</span>
                <div id="status-bar-battery" class="battery-container">
                    <span class="battery-text">--%</span>
                    <div class="battery-icon">
                        <div class="battery-level"></div>
                    </div>
                </div>
            </div>
            <div id="notification-bar"><img id="notification-avatar" src=""><div id="notification-content"><div class="name"></div><div class="message"></div></div></div>

<!-- ▼▼▼ 【全新】在这里粘贴悬浮歌词栏的代码 ▼▼▼ -->
<div id="floating-lyrics-bar">
    <span id="floating-lyric-text">♪</span>
    <!-- 【问题4修复】用包含SVG的div替换旧的span -->
    <div id="lyrics-settings-btn" style="cursor: pointer; display: flex; align-items: center; justify-content: center;">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="3"></circle>
            <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
        </svg>
    </div>
    <span class="close-btn">×</span>
</div>
<!-- ▲▲▲ 粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 在这里粘贴下面这行新代码 ▼▼▼ -->
<div id="lock-screen-background-blur" style="display: none;"></div>
<!-- ▲▲▲ 粘贴结束 ▲▲▲ -->
            
            <div id="home-screen" class="screen active">
<!-- ▼▼▼ 【全新】滑动容器与分页结构 ▼▼▼ -->
<div class="home-screen-slider">
    <!-- 第一页 -->
    <div class="home-screen-page">
        <div id="clock-container">
            <div id="main-time">12:00</div>
            <div id="main-date">星期一, 1月1日</div>
        </div>
        <div class="app-grid">
            <!-- 第一行：放2个图标 -->
            <div class="app-row">
                <div class="app-icon" onclick="showScreen('world-book-screen')">
                    <div class="icon-bg">
                        <img id="icon-img-world-book" src="https://i.postimg.cc/HWf1JKzn/IMG-6435.jpg" alt="世界书">
                    </div>
                    <span class="label">世界书</span>
                </div>
                <div class="app-icon" onclick="showScreen('chat-list-screen')">
                    <div class="icon-bg">
                        <img id="icon-img-qq" src="https://i.postimg.cc/MTC3Tkw8/IMG-6436.jpg" alt="QQ">
                    </div>
                    <span class="label">QQ</span>
                </div>
            </div>
            <!-- 第二行：放3个图标 -->
            <div class="app-row">
                <div class="app-icon" onclick="showScreen('api-settings-screen')">
                    <div class="icon-bg">
                        <img id="icon-img-api-settings" src="https://i.postimg.cc/MK8rJ8t7/IMG-6438.jpg" alt="API设置">
                    </div>
                    <span class="label">API设置</span>
                </div>
                <div class="app-icon" onclick="showScreen('wallpaper-screen')">
                    <div class="icon-bg">
                        <img id="icon-img-wallpaper" src="https://i.postimg.cc/T1j03pQr/IMG-6440.jpg" alt="外观设置">
                    </div>
                    <span class="label">外观设置</span>
                </div>
                <div class="app-icon" onclick="showScreen('font-settings-screen')">
                    <div class="icon-bg">
                        <img id="icon-img-font" src="https://i.postimg.cc/pXxk1JXk/IMG-6442.jpg" alt="字体">
                    </div>
                    <span class="label">字体</span>
                </div>
            </div>
        </div>
    </div>

<!-- ▼▼▼ 这是修改后的新结构 ▼▼▼ -->
<div class="home-screen-page">
    <!-- 1. 在这里添加了新类名 app-grid-standard -->
    <!-- 2. 并且删除了里面的 app-row -->
    <div class="app-grid app-grid-standard">
        <div class="app-icon" data-app-id="check-phone" onclick="openCharacterSelectionScreen()">
            <div class="icon-bg">
                <img id="icon-img-check-phone" src="https://i.postimg.cc/RVwpwr0r/IMG-8348.jpg" alt="查手机">
            </div>
            <span class="label">查手机</span>
        </div>
        
        <!-- 你未来添加的新APP图标，也直接放在这里就行 -->
        <!-- 
        <div class="app-icon">
            ...
        </div>
        -->
    </div>
</div>
<!-- ▲▲▲ 修改结束 ▲▲▲ -->
</div>

<!-- 分页小圆点 -->
<div class="pagination-dots">
    <span class="dot active"></span>
    <span class="dot"></span>
</div>
<!-- ▲▲▲ 新结构结束 ▲▲▲ -->
            </div>
          
<div id="world-book-screen" class="screen">
    <div class="header">
        <span class="back-btn" onclick="showScreen('home-screen')">‹</span>
        <span>世界书</span>
        <div class="header-actions"> <!-- 【推荐】用一个容器把按钮包起来 -->
            <span class="action-btn" id="manage-world-book-categories-btn">管理分类</span>
            <span class="action-btn" id="add-world-book-btn">+</span>
        </div>
    </div>
    <div id="world-book-list"></div>
</div>
            <div id="world-book-editor-screen" class="screen">
                <div class="header">
                    <span class="back-btn" onclick="showScreen('world-book-screen')">‹</span>
                    <span id="world-book-editor-title">编辑世界书</span>
                    <span class="save-btn" id="save-world-book-btn">保存</span>
                </div>
                <div class="form-container">
                    <div class="form-group">
                        <label for="world-book-name-input">书名</label>
                        <input type="text" id="world-book-name-input" placeholder="请输入世界书的名称...">
                    </div>

        <!-- ▼▼▼ 【全新】在这里添加分类选择 ▼▼▼ -->
        <div class="form-group">
            <label for="world-book-category-select">分类</label>
            <select id="world-book-category-select">
                <!-- 选项将由JS动态生成 -->
            </select>
        </div>
        <!-- ▲▲▲ 添加结束 ▲▲▲ -->

                    <div class="form-group" style="height: 100%;">
                        <label for="world-book-content-input">内容</label>
                        <textarea id="world-book-content-input" placeholder="在此处输入详细的世界观设定..."></textarea>
                    </div>
                </div>
            </div>

            <div id="api-settings-screen" class="screen"><div class="header"><span class="back-btn" onclick="showScreen('home-screen')">‹</span><span>API 设置</span><span style="width: 30px;"></span></div><div class="form-container"><p style="font-size: 14px; color: #666; background-color: #f0f0f0; padding: 10px; border-radius: 8px;">提示: 若要使用“发送图片”功能, 请务必选择支持Vision(视觉)的模型, 如<code style="background-color: #e0e0e0; padding: 2px 4px; border-radius: 4px;">gpt-4o</code>或<code style="background-color: #e0e0e0; padding: 2px 4px; border-radius: 4px;">gpt-4-vision-preview</code>。</p><div class="form-group"><label for="proxy-url">反代地址 (不需要添加/v1噢~)</label><input type="text" id="proxy-url" placeholder="例如: https://api.openai.com"></div><div class="form-group"><label for="api-key">密钥 (直连轮询用英文逗号隔开)</label><input type="password" id="api-key" placeholder="sk-..."></div><div class="form-group"><label for="model-select">模型</label><select id="model-select"></select></div>
<!-- ▼▼▼ 请将这段【全新的HTML代码】粘贴到 “模型” 下拉框的 form-group 之后 ▼▼▼ -->
<div class="form-group">
    <label>API 预设</label>
    <div class="bubble-preset-manager"> <!-- 复用现有样式 -->
        <select id="api-preset-select" class="form-group select"></select>
        <button id="manage-api-presets-btn" class="action-btn">管理</button>
    </div>
</div>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->
<button class="form-button" id="fetch-models-btn">拉取模型</button>

<!-- ▼▼▼ 将这段代码粘贴到 API 设置页面的“保存设置”按钮上方 ▼▼▼ -->
<hr style="margin:20px 0; opacity:.3">
<div class="form-group" style="display: flex; justify-content: space-between; align-items: center;">
    <label for="background-activity-switch" style="margin-bottom: 0;">
        启用后台角色活动
        <p style="font-size: 12px; font-weight: normal; color: #ff6b6b; margin-top: 5px;">
            警告：此功能会显著增加API调用和费用！
        </p>
    </label>
    <input type="checkbox" id="background-activity-switch" style="width: auto; height: 20px;">
</div>
<!-- ▲▲▲ 粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 将这段代码粘贴到“启用后台角色活动”开关的下方 ▼▼▼ -->
<div class="form-group" style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
    <label for="background-interval-input" style="margin-bottom: 0;">
        后台活动检测间隔 (秒)
        <p style="font-size: 12px; font-weight: normal; color: #999; margin-top: 5px;">
            建议值 60-300。值越大，费用越低，但角色反应越慢。
        </p>
    </label>
    <input type="number" id="background-interval-input" min="30" value="60" style="width: 80px; text-align: center;">
</div>
<!-- ▲▲▲ 粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 在这里新增 ▼▼▼ -->
<div class="form-group" style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
    <label for="block-cooldown-input" style="margin-bottom: 0;">
        AI被拉黑后冷静期 (小时)
        <p style="font-size: 12px; font-weight: normal; color: #999; margin-top: 5px;">
            被拉黑超过这个时间后，AI才有几率重新申请好友。
        </p>
    </label>
    <input type="number" id="block-cooldown-input" min="0.1" step="0.1" value="1" style="width: 80px; text-align: center;">
</div>
<!-- ▲▲▲ 新增结束 ▲▲▲ -->

<button class="form-button" id="save-api-settings-btn">保存设置</button>
			<hr style="margin:20px 0; opacity:.3">

			<button class="form-button" id="export-data-btn">导出数据</button>

<!-- ① 普通按钮，和“导出”一个 class -->
<button class="form-button" id="import-btn">导入备份文件</button>

<!-- ② 真正的文件选择器，完全隐藏 -->
<input id="import-data-input" type="file" accept="application/json" hidden>
</div></div>
<!-- ▼▼▼ 用下面这段代码，完整替换掉你原来的 chat-list-screen ▼▼▼ -->
<div id="chat-list-screen" class="screen">
    
    <!-- 主头部 (只在消息列表显示) -->
    <div class="header" id="main-chat-list-header">
        <span class="back-btn" onclick="showScreen('home-screen')">‹</span>
<span id="chat-list-title">消息</span>
        <div class="header-actions">
            <span class="action-btn" id="add-group-chat-btn" title="创建群聊"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 17.5C19.1569 17.5 20.5 16.1569 20.5 14.5C20.5 12.8431 19.1569 11.5 17.5 11.5C15.8431 11.5 14.5 12.8431 14.5 14.5C14.5 16.1569 15.8431 17.5 17.5 17.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 21L19 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.5 11.5C10.1569 11.5 11.5 10.1569 11.5 8.5C11.5 6.84315 10.1569 5.5 8.5 5.5C6.84315 5.5 5.5 6.84315 5.5 8.5C5.5 10.1569 6.84315 11.5 8.5 11.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12.5 14.5H4.5C3.39543 14.5 2.5 15.3954 2.5 16.5V18.5H12.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
            <span class="action-btn" id="add-chat-btn">+</span>
        </div>
    </div>

    <!-- 消息列表视图 -->
    <div id="messages-view" class="chat-list-view active">
        <div id="chat-list">
            <!-- JS会在这里生成聊天列表 -->
        </div>
    </div>

    <!-- 动态界面视图 -->
    <div id="qzone-screen" class="chat-list-view">
        <div class="qzone-header">
            <span class="back-btn" id="qzone-back-btn">‹</span> <!-- 这个按钮现在只负责从动态返回 -->
            <span>好友动态</span>
        </div>
        <div class="qzone-content">
            <div class="qzone-profile-header">
                <div id="qzone-banner-container" class="qzone-banner-container">
                    <img id="qzone-banner-img" src="https://files.catbox.moe/r5heyt.gif" alt="背景">
                    <input type="file" id="qzone-banner-input" accept="image/*" hidden>
                </div>
                <div class="qzone-user-info">
                    <div id="qzone-avatar-container" class="qzone-avatar-container">
                        <img id="qzone-avatar-img" src="https://files.catbox.moe/q6z5fc.jpeg" alt="头像">
                        <input type="file" id="qzone-avatar-input" accept="image/*" hidden>
                    </div>
                    <span id="qzone-nickname">{{user}}</span>
                </div>
            </div>
            <div class="qzone-actions-bar">
                <div class="action-item" id="create-shuoshuo-btn"><span>说说</span></div>
                <div class="action-item" id="create-post-btn"><span>动态</span></div>
                <div class="action-item" id="open-album-btn"><span>相册</span></div>
            </div>
            <div id="qzone-posts-list"></div>
        </div>
    </div>

    <!-- 收藏界面视图 -->
    <div id="favorites-view" class="chat-list-view">
    <div class="header"> 
        <span class="back-btn" id="favorites-back-btn">‹</span>
        <span>我的收藏</span>
        <!-- 新增的编辑按钮 -->
        <span class="action-btn" id="favorites-edit-btn">编辑</span>
    </div>

        <!-- 【新增】搜索栏容器 -->
        <div class="search-bar-container">
            <input type="search" id="favorites-search-input" placeholder="搜索收藏的标题、内容或作者...">
            <button id="favorites-search-clear-btn" class="search-clear-btn" style="display: none;">×</button>
        </div>

        <div id="favorites-list" class="list-container">
            <!-- 收藏内容将由JS动态生成在这里 -->
        </div>

<!-- 新增：收藏页底部操作栏 -->
<div id="favorites-action-bar" style="display: none;">
    <button id="favorites-delete-selected-btn" class="action-bar-btn">删除 (0)</button>
</div>

    </div>

<!-- ▼▼▼ 【全新】回忆录界面视图 ▼▼▼ -->
<div id="memories-view" class="chat-list-view">
    <div class="header"> 
        <span class="back-btn" id="memories-back-btn">‹</span>
        <span>我们的回忆</span>
            <span class="action-btn" id="add-countdown-btn">+</span>
        </div>
    <div id="memories-list" class="list-container" style="padding: 15px; display: flex; flex-direction: column; gap: 15px;">
        <!-- 回忆卡片将由JS动态生成在这里 -->
    </div>
</div>
<!-- ▲▲▲ 新增HTML结束 ▲▲▲ -->
    
    <!-- 底部导航栏 -->
<div id="chat-list-bottom-nav">
    <div class="nav-item active" data-view="messages-view">
        <span>消息</span>
    </div>
    <div class="nav-item" data-view="qzone-screen">
        <span>动态</span>
    </div>
    <!-- ▼▼▼ 在“动态”和“收藏”之间，加入这个新页签 ▼▼▼ -->
    <div class="nav-item" data-view="memories-view">
        <span>回忆</span>
    </div>
    <!-- ▲▲▲ 添加结束 ▲▲▲ -->
    <div class="nav-item" data-view="favorites-view">
        <span>收藏</span>
    </div>
</div>
</div>
<!-- ▲▲▲ 替换区域结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这段新的 HTML 粘贴到 id="chat-list-screen" 的 div 之后 ▼▼▼ -->
<div id="album-screen" class="screen">
    <!-- 1. 页面头部，包含返回按钮和标题 -->
    <div class="header">
        <span class="back-btn" id="album-back-btn">‹</span>
        <span>我的相册</span>
        <span class="action-btn" id="create-album-btn-page">+</span>
    </div>
    
    <!-- 2. 页面内容容器 -->
    <div class="list-container">
        <div id="album-grid-page">
            <!-- 相册列表将由 JS 动态生成在这里 -->
        </div>
    </div>
</div>
<!-- ▲▲▲ 新的 HTML 粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这段新的 HTML 粘贴到 id="album-screen" 的 div 之后 ▼▼▼ -->
<div id="album-photos-screen" class="screen">
    <!-- 1. 页面头部 -->
    <div class="header">
        <span class="back-btn" id="album-photos-back-btn">‹</span>
        <span id="album-photos-title">相册名称</span>
        <span class="action-btn" id="album-upload-photo-btn">上传</span>
    </div>
    
    <!-- 2. 页面内容容器 -->
    <div class="list-container">
        <div id="photos-grid-page">
            <!-- 照片列表将由 JS 动态生成在这里 -->
        </div>

<!-- ▼▼▼ 请将这段新的 HTML 粘贴到所有模态框的末尾 ▼▼▼ -->
<div id="photo-viewer-modal" class="modal">
    <!-- 1. 关闭按钮 -->
    <button id="photo-viewer-close-btn">×</button>
    
    <!-- 2. 上一张照片按钮 -->
    <button id="photo-viewer-prev-btn" class="nav-arrow">‹</button>
    
    <!-- 3. 图片容器 -->
    <div class="photo-viewer-content">
        <img id="photo-viewer-image" src="" alt="全屏照片预览">
    </div>
    
    <!-- 4. 下一张照片按钮 -->
    <button id="photo-viewer-next-btn" class="nav-arrow">›</button>
</div>
<!-- ▲▲▲ 新的 HTML 粘贴结束 ▲▲▲ -->

    </div>
</div>
<!-- ▲▲▲ 新的 HTML 粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 粘贴到 #album-photos-screen 的 div 之后 ▼▼▼ -->
<input type="file" id="album-photo-input" accept="image/*" multiple hidden>
            
<!-- ▼▼▼ 请用这【一整块】全新的代码，完整替换掉您文件中旧的 #chat-interface-screen 及其所有内容 ▼▼▼ -->
<div id="chat-interface-screen" class="screen">

    <!-- 【最终修正版】Header，已将状态栏和搜索功能正确整合 -->
    <div class="header">
        <!-- 默认控件：包含标题、状态栏和常规按钮 -->
        <div class="default-controls">
            <span class="back-btn" id="back-to-list-btn">‹</span>
            
            <!-- ▼▼▼ 【核心新增】标题和状态的容器 ▼▼▼ -->
            <div id="chat-header-title-wrapper">
                <span id="chat-header-title">聊天对象</span>
                <div id="chat-header-status">
                    <span class="status-dot"></span>
                    <span class="status-text">在线</span>
                </div>
            </div>
            <!-- ▲▲▲ 新增结束 ▲▲▲ -->

            <div class="header-actions">
                <span class="action-btn" id="listen-together-btn" title="一起听"><img src="https://i.postimg.cc/dV8sdNcx/210-20250618115221.png" alt="一起听"></span>
                <span class="action-btn" id="chat-settings-btn" title="聊天设置"><img src="https://i.postimg.cc/bvPq64cv/CCA834-BA-5-A90-408-D-94-FA-7-EE156-B6-A765.png" alt="设置"></span>
            </div>
        </div>

        <!-- 多选模式控件 (保持不变) -->
        <div class="selection-controls">
            <span id="selection-cancel-btn">取消</span>
            <span id="selection-count"></span>
            <div class="header-actions">
               <span id="selection-favorite-btn" class="action-btn">收藏</span>
       <span id="selection-share-btn" class="action-btn">分享</span> 
               <span id="selection-delete-btn" class="action-btn" style="color: #ff3b30;">删除</span>
            </div>
        </div>
    </div>
    
    <!-- 聊天消息区域 (保持不变) -->
    <div id="chat-messages"><div id="typing-indicator">对方正在输入...</div></div>

    <!-- 输入区域 (保持不变) -->
    <div id="chat-input-area">
<div id="reply-preview-bar">
    <div class="reply-preview-content">
        <div class="sender">回复 xxx:</div>
        <div class="text">被引用的消息内容...</div>
    </div>
    <span id="cancel-reply-btn">×</span>
</div>
        <div id="chat-input-actions-top">
            <button id="open-sticker-panel-btn" class="chat-action-icon-btn action-button" title="表情面板"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
    <line x1="12" y1="5" x2="12" y2="19"></line>
    <line x1="5" y1="12" x2="19" y2="12"></line>
</svg></button>
            <button id="send-photo-btn" class="chat-action-icon-btn action-button" title="发送照片"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg></button>
            <button id="upload-image-btn" class="chat-action-icon-btn action-button" title="上传图片"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" ><path d="M21 3.5H3C2.44772 3.5 2 3.94772 2 4.5V19.5C2 20.0523 2.44772 20.5 3 20.5H21C21.5523 20.5 22 20.0523 22 19.5V4.5C22 3.94772 21.5523 3.5 21 3.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M16.5 13.5C17.6046 13.5 18.5 12.6046 18.5 11.5C18.5 10.3954 17.6046 9.5 16.5 9.5C15.3954 9.5 14.5 10.3954 14.5 11.5C14.5 12.6046 15.3954 13.5 16.5 13.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M22 14.5L18 10.5L10.3333 18.5M12.5 16L9 12.5L2 19.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
<button id="transfer-btn" class="chat-action-icon-btn action-button" title="转账">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
    <path d="M7 4L12 12L17 4M12 12V20M8 10H16M8 13H16"></path>
</svg>
</button>
            <button id="voice-message-btn" class="chat-action-icon-btn action-button" title="发送语音"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><path d="M12 19v4"/><path d="M8 23h8"/></svg></button>
<!-- ▼▼▼ 将这行新代码粘贴到“发送语音”按钮的后面 ▼▼▼ -->
<button id="send-waimai-request-btn" class="chat-action-icon-btn action-button" title="发起外卖请求"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg></button>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->
<!-- ▼▼▼ 【新增】视频通话按钮 ▼▼▼ -->
<button id="video-call-btn" class="chat-action-icon-btn action-button" title="视频通话"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg></button>
<!-- ▲▲▲ 新增结束 ▲▲▲
<!-- ▼▼▼群视频通话按钮 ▼▼▼ -->
<button id="group-video-call-btn" class="chat-action-icon-btn action-button" title="群视频通话"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg></button>
<!-- ▲▲▲ 全新添加结束 ▲▲▲ -->
<!-- ▼▼▼发起投票按钮▼▼▼ -->
<button id="send-poll-btn" class="chat-action-icon-btn action-button" title="发起投票"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6h10"/><path d="M6 6h.01"/><path d="M8 12h10"/><path d="M6 12h.01"/><path d="M8 18h10"/><path d="M6 18h.01"/></svg></button>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->
<!-- ▼▼▼ 在 id="chat-input-actions-top" 的末尾，【添加】这个新的按钮 ▼▼▼ -->
<button id="share-link-btn" class="chat-action-icon-btn action-button" title="分享链接"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72"></path></svg></button>
<!-- ▲▲▲ 新增HTML结束 ▲▲▲ -->
<!-- ▼▼▼ 【全新】发送定位按钮 ▼▼▼ -->
<button id="send-location-btn" class="chat-action-icon-btn action-button" title="发送定位">
    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
        <circle cx="12" cy="10" r="3"></circle>
    </svg>
</button>
<!-- ▲▲▲ 新增结束 ▲▲▲ -->
        </div>
        <div id="chat-input-main-row">
            <textarea id="chat-input" rows="1" placeholder="输入消息..."></textarea>
            <div id="input-actions-wrapper">
                <button id="wait-reply-btn" title="等待回复"><img src="https://i.postimg.cc/q72zq80N/ECE92-BBC-BE57-48-E9-BB2-C-345-B6019-C4-B2.png" alt="等待回复"></button>
    <!-- ▼▼▼ 在这里粘贴下面这行新代码 ▼▼▼ -->
    <button id="reroll-btn" class="action-button" title="重新生成回复"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
    <polyline points="23 4 23 10 17 10"></polyline>
    <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
</svg></button>
    <!-- ▲▲▲ 粘贴结束 ▲▲▲ -->
                <button id="send-btn" class="action-button">发送</button>
            </div>
        </div>
    </div>

<!-- ▼▼▼ 在这里新增 ▼▼▼ -->
<div id="chat-lock-overlay">
    <div id="chat-lock-content"></div>
</div>
<!-- ▲▲▲ 新增结束 ▲▲▲ -->

    <!-- 表情面板 (保持不变) -->
    <div id="sticker-panel">
        <div id="sticker-panel-header">
            <span class="panel-btn" id="close-sticker-panel-btn">取消</span>
            <span class="title">我的表情</span>
            <div style="display: flex; gap: 10px;">
              <span class="panel-btn" id="add-sticker-btn">添加</span>
              <span class="panel-btn" id="upload-sticker-btn">上传</span>
            </div>
        </div>
        <div id="sticker-grid"></div>
    </div>
<input type="file" id="sticker-upload-input" accept="image/*" style="display: none;" multiple>
    <input type="file" id="image-upload-input" accept="image/*" style="display: none;">
    
    <!-- 音乐播放器 (保持不变) -->
<div id="music-player-overlay">
    <div class="music-player-window">
        
        <!-- 1. 顶部操作栏 -->
        <div class="music-player-top-actions">
            <div class="top-left-cluster">
                <button id="music-return-btn">‹</button>
                <button id="music-exit-btn">×</button>
            </div>
            <span id="music-playlist-btn">☰</span>
        </div>
        
        <!-- 2. 歌曲信息 -->
        <div id="music-time-counter">已经一起听了0.0小时</div>
        <div id="music-player-song-title">请添加歌曲</div>
        <div id="music-player-artist">...</div>
        
        <!-- 3. 【全新】歌词显示区域 -->
        <div id="music-lyrics-container">
            <div id="music-lyrics-list">
                <!-- 歌词将由JS动态生成在这里 -->
                <div class="lyric-line">♪ 暂无歌词 ♪</div>
            </div>
        </div>
        
        <!-- 4. 【全新】播放控制区的包裹容器 -->
        <div class="music-player-controls-wrapper">
            <!-- a. 新的iOS风格进度条 -->
            <div class="music-progress-bar-container">
                <div id="music-current-time" class="time-display">0:00</div>
                <div class="progress-bar">
                    <div id="music-progress-fill" class="progress-bar-fill"></div>
                </div>
                <div id="music-total-time" class="time-display">0:00</div>
            </div>
            
            <!-- b. 播放控制按钮 -->
            <div class="music-controls">
                <button id="music-prev-btn">◀</button>
                <button id="music-play-pause-btn" class="play-pause-btn">▶</button>
                <button id="music-next-btn">▶</button>
                <button id="music-mode-btn">顺序</button>
    <!-- ▼▼▼ 在这里粘贴新按钮 ▼▼▼ -->
    <button id="toggle-lyrics-bar-btn" title="桌面歌词">悬浮</button>
    <!-- ▲▲▲ 粘贴结束 ▲▲▲ -->
            </div>
        </div>

    </div>
</div>
    
    <div id="music-playlist-panel">
        <div class="playlist-header">
            <span class="panel-btn" id="close-playlist-btn">返回</span>
            <span>播放列表</span>
            <div>
                <span class="panel-btn" id="add-song-local-btn">本地</span>
                <span class="panel-btn" id="add-song-url-btn">URL</span>
            </div>
        </div>
        <div class="playlist-body" id="playlist-body"></div>
    </div>
    <input type="file" id="local-song-upload-input" accept="audio/*" multiple style="display: none;">
<input type="file" id="lrc-upload-input" accept=".lrc" style="display: none;">
</div>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->

<!-- ▼▼▼ 用这整块代码替换你原来的 id="wallpaper-screen" ▼▼▼ -->
<div id="wallpaper-screen" class="screen">
    <div class="header">
        <span class="back-btn" onclick="showScreen('home-screen')">‹</span>
        <!-- 【核心修改1】标题改为“外观设置”，更通用 -->
        <span>外观设置</span>
        <span style="width: 30px;"></span>
    </div>
    <div class="form-container">
        <!-- 锁屏壁纸设置 (全新) -->
        <div style="width:100%; text-align: left; margin-bottom: 5px;">
            <label style="font-weight: 500; color: var(--text-secondary);">锁屏设置</label>
        </div>
        <div id="lockscreen-wallpaper-preview" class="wallpaper-preview">点击下方上传</div>
        <button class="form-button" onclick="document.getElementById('lockscreen-wallpaper-upload-input').click();">上传锁屏壁纸</button>
        <input type="file" id="lockscreen-wallpaper-upload-input" accept="image/*" hidden>
        
        <div class="form-group" style="width: 100%; margin-top: 15px;">
            <label for="password-set-input">锁屏密码 (留空则无密码)</label>
            <input type="text" id="password-set-input" placeholder="设置你的解锁密码">
        </div>
        
        <hr style="width: 80%; opacity: 0.3; margin: 20px 0;">

        <!-- 主屏幕壁纸设置 (原有) -->
        <div style="width:100%; text-align: left; margin-bottom: 5px;">
            <label style="font-weight: 500; color: var(--text-secondary);">主屏幕设置</label>
        </div>
        <div id="wallpaper-preview">点击下方上传</div>
        <button class="form-button" onclick="document.getElementById('wallpaper-upload-input').click();">上传壁纸</button>
        <input type="file" id="wallpaper-upload-input" accept="image/*">

<!-- ▼▼▼ 将【上面整块代码】，完整替换为下面这段【全新的代码】 ▼▼▼ -->
<div class="form-group" style="display: flex; justify-content: space-between; align-items: center;">
    <label for="theme-toggle-switch" style="margin-bottom: 0;">夜间模式</label>
    <label class="toggle-switch">
        <input type="checkbox" id="theme-toggle-switch">
        <span class="slider"></span>
    </label>
</div>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->

    <!-- === ▼▼▼ 以下是新增的美化功能UI ▼▼▼ === -->
    <div style="width:100%; text-align: left; margin-bottom: 15px;">
        <label style="font-weight: 500; color: var(--text-secondary);">手机美化 (CSS)</label>
    </div>

    <!-- 主题选择和管理 -->
    <div class="form-group">
        <label for="theme-selector">选择已存方案</label>
        <div style="display: flex; gap: 10px;">
            <select id="theme-selector" style="flex-grow: 1;"></select>
            <button id="rename-theme-btn" class="form-button-secondary" style="margin:0; padding: 0 10px;">重命名</button>
            <button id="delete-theme-btn" class="form-button-secondary" style="margin:0; padding: 0 10px; background-color: #ffdddd; color: #ff3b30;">删除</button>
        </div>
    </div>

    <!-- CSS 代码编辑区 -->
    <div class="form-group">
        <label for="theme-css-editor">美化代码编辑区</label>
        <textarea id="theme-css-editor" rows="10" style="font-family: monospace; font-size: 12px; resize: vertical;" placeholder="在这里粘贴或编辑美化代码..."></textarea>
    </div>
    
    <!-- 操作按钮 -->
    <button id="apply-theme-btn" class="form-button">应用当前代码</button>
    <div style="display: flex; gap: 10px; margin-top: 10px;">
        <button id="save-theme-btn" class="form-button-secondary" style="flex:1;">保存当前方案</button>
        <button id="save-as-new-theme-btn" class="form-button-secondary" style="flex:1;">另存为新方案</button>
    </div>
    <div style="display: flex; gap: 10px; margin-top: 10px;">
        <button id="export-theme-btn" class="form-button-secondary" style="flex:1;">导出方案</button>
        <button id="import-theme-btn" class="form-button-secondary" style="flex:1;">导入方案</button>
        <input type="file" id="import-theme-input" accept=".json" hidden>
    </div>
    <!-- === ▲▲▲ 新增UI结束 ▲▲▲ === -->
        
        <!-- 【核心修改2】新增图标设置区域 -->
        <hr style="width: 80%; opacity: 0.3; margin: 30px 0;">
        <div style="width:100%; text-align: left; margin-bottom: 15px;">
            <label style="font-weight: 500; color: var(--text-secondary);">App 图标设置</label>
        </div>
        <div id="icon-settings-grid">
            <!-- 图标设置项将由JS动态生成在这里 -->
        </div>

<!-- ▼▼▼ 将这段新代码，粘贴到“保存所有外观设置”按钮的【上方】 ▼▼▼ -->
<hr style="width: 80%; opacity: 0.3; margin: 30px 0;">
<div style="width:100%; text-align: left; margin-bottom: 15px;">
    <label style="font-weight: 500; color: var(--text-secondary);">铃声设置</label>
</div>
<div class="form-group" style="width: 100%;">
    <label for="ringtone-url-input">来电铃声 URL (.mp3, .wav, etc.)</label>
    <input type="text" id="ringtone-url-input" placeholder="输入音频文件的网络链接...">
</div>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->
        
        <!-- 【核心修改3】按钮文字也改一下 -->
        <button class="form-button" id="save-wallpaper-btn" style="margin-top: 30px;">保存所有外观设置</button>
    </div>
</div>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】分享链接功能 HTML ▼▼▼ -->
<div id="browser-screen" class="screen">
    <div class="header">
        <span class="back-btn" id="browser-back-btn">‹</span>
        <span id="browser-title"></span>
        <span style="width: 30px;"></span>
    </div>
    <div id="browser-content" class="list-container">
        <!-- 文章内容将由JS动态生成在这里 -->
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<div id="font-settings-screen" class="screen">
    <div class="header">
        <span class="back-btn" onclick="showScreen('home-screen')">‹</span>
        <span>字体设置</span>
        <span style="width: 30px;"></span>
    </div>
    <div class="form-container">
        <div class="form-group">
            <label for="font-url-input">字体文件URL (.ttf, .otf, .woff等)</label>
            <input type="text" id="font-url-input" placeholder="https://..../font.ttf">
        </div>

        <div class="form-group">
            <label>实时预览</label>
<div id="font-preview">
                <p style="font-size: 20px; margin: 0 0 10px 0;">你好世界 Hello World</p>
                <p style="margin: 0;">这是字体预览效果，12345。</p>
            </div>
        </div>

        <button class="form-button" id="save-font-btn">保存并应用</button>
        <button class="form-button form-button-secondary" id="reset-font-btn">恢复默认字体</button>
    </div>
</div>

<!-- ▼▼▼ 【全新】“查角色手机”功能的所有HTML界面 (V3终极版) ▼▼▼ -->

<!-- 1. 角色选择屏幕 (保持不变) -->
<div id="character-selection-screen" class="screen">
    <div class="header">
        <span class="back-btn" onclick="showScreen('home-screen')">‹</span>
        <span>选择要查看的手机</span>
        <span style="width: 30px;"></span>
    </div>
    <div id="character-selection-list" class="list-container"></div>
</div>

<!-- 2. 角色的手机“外壳”容器 -->
<div id="character-phone-container" class="screen">
    <div class="character-phone-frame">
        <div class="character-phone-notch"></div>
        <div class="character-phone-inner-screen">
            
            <!-- 2a. 角色手机的主界面 -->
            <div id="character-phone-screen" class="character-phone-page active">
                <div class="header character-phone-header">
                    <!-- 【核心修改】移除了onclick，换成data-target-page -->
                    <span class="back-btn" data-target-screen="character-selection-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span id="character-phone-owner-name"></span>
                    <div class="header-actions">
                        <span class="action-btn" id="clear-character-data-btn" title="清空数据">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>
                        </span>
                        <span class="action-btn" id="generate-character-data-btn" title="刷新数据">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 4v6h-6M1 20v-6h6"/><path d="M3.51 9a9 9 0 0114.85-3.36L20.5 10M3.5 14a9 9 0 0114.85 3.36L20.5 14"/></svg>
                        </span>
                    </div>
                </div>
                <div id="character-app-grid" class="app-grid app-grid-standard" style="padding-top: 60px;"></div>
            </div>

            <!-- 2b. 角色手机 - 聊天列表 -->
            <div id="character-chat-list-screen" class="character-phone-page">
                <div class="header character-phone-header">
                    <!-- 【核心修改】移除了onclick，换成data-target-page -->
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>消息</span>
                </div>
                <div id="character-chat-list" class="list-container" style="padding: 0;"></div>
            </div>

            <!-- 2c. 角色手机 - 具体聊天记录 -->
            <div id="character-chat-history-screen" class="character-phone-page">
                <div class="header character-phone-header">
                     <!-- 【核心修改】移除了onclick，换成data-target-page -->
                     <span class="back-btn" data-target-page="character-chat-list-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span id="character-chat-with-name"></span>
                </div>
                <div id="character-chat-history-messages" class="list-container" style="padding: 15px; display: flex; flex-direction: column; gap: 15px; background-color: #e5ddd5;"></div>
            </div>

            <!-- 2d. 角色手机 - 购物车 -->
            <div id="character-shopping-cart-screen" class="character-phone-page">
                <div class="header character-phone-header">
                    <!-- 【核心修改】移除了onclick，换成data-target-page -->
                    <span class="back-btn" data-target-page="character-phone-screen">
                         <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>购物车</span>
                </div>
                <div id="character-shopping-cart-list" class="list-container"></div>
            </div>
            
            <!-- 2e. 角色手机 - 备忘录 -->
            <div id="character-memos-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <!-- 【核心修改】移除了onclick，换成data-target-page -->
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>备忘录</span>
                </div>
                <div id="character-memos-list" class="list-container"></div>
            </div>

            <!-- 2f. 角色手机 - 浏览器 -->
            <div id="character-browser-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>浏览器</span>
                </div>
                <div id="character-browser-list" class="list-container"></div>
            </div>

            <!-- 2k. 角色手机 - 浏览器搜索结果详情 -->
            <div id="character-browser-detail-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-browser-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span id="character-browser-detail-title">搜索结果</span>
                </div>
                <div id="character-browser-detail-content" class="list-container" style="padding: 15px; line-height: 1.7;"></div>
            </div>

            <!-- 2g. 角色手机 - 相册 -->
            <div id="character-album-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>相册</span>
                </div>
                <div id="character-album-grid" class="list-container"></div>
            </div>
            
            <!-- 2h. 角色手机 - 银行 -->
            <div id="character-bank-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>钱包</span>
                </div>
                <div id="character-bank-details" class="list-container"></div>
            </div>
            
            <!-- 2i. 角色手机 - 行动轨迹 -->
            <div id="character-trajectory-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>足迹</span>
                </div>
                <div id="character-trajectory-list" class="list-container"></div>
            </div>
            
            <!-- 2j. 角色手机 - APP使用记录 -->
            <div id="character-app-usage-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>屏幕使用时间</span>
                </div>
                <div id="character-app-usage-list" class="list-container"></div>
            </div>

            <!-- 2l. 角色手机 - 日记 -->
            <div id="character-diary-screen" class="character-phone-page">
                 <div class="header character-phone-header">
                    <span class="back-btn" data-target-page="character-phone-screen">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
                    </span>
                    <span>日记</span>
                    <div class="header-actions">
                        <!-- 这是日记专属的刷新按钮 -->
                        <span class="action-btn" id="generate-diary-entry-btn" title="写新日记">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
                        </span>
                    </div>
                </div>
                <div id="character-diary-list" class="list-container"></div>
            </div>

        </div>
    </div>
</div>

<!-- 加载动画遮罩层 (保持不变) -->
<div id="generation-overlay" class="modal" style="background-color: rgba(0,0,0,0.6); z-index: 2000;">
    <div style="text-align: center; color: white;">
        <div id="loading-spinner" style="width: 40px; height: 40px; border: 4px solid rgba(255,255,255,0.3); border-top-color: white; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px;"></div>
        <p>正在同步Ta的手机数据...</p>
        <p style="font-size: 12px; opacity: 0.7;">（这可能需要一些时间，并会消耗API额度）</p>
    </div>
</div>

<!-- ▲▲▲ “查角色手机”功能HTML结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】选择联系人以创建群聊的屏幕 ▼▼▼ -->
<div id="contact-picker-screen" class="screen">
    <div class="header">
        <span class="back-btn" id="cancel-contact-picker-btn">取消</span>
        <span>选择联系人</span>
        <span class="save-btn" id="confirm-contact-picker-btn">完成(0)</span>
    </div>
    <div class="list-container" id="contact-picker-list">
        <!-- 联系人列表将由JS动态生成 -->
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】群成员管理屏幕 ▼▼▼ -->
<div id="member-management-screen" class="screen">
    <div class="header">
        <span class="back-btn" id="back-from-member-management">‹</span>
        <span>群成员管理</span>
        <span style="width: 30px;"></span>
    </div>
    <div class="list-container" id="member-management-list">
        <!-- 现有成员列表会在这里动态生成 -->
    </div>
    <div id="member-management-actions">
        <button id="add-existing-contact-btn">从好友列表添加</button>
        <button id="create-new-member-btn">创建群内新成员</button>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】来电请求模态框 ▼▼▼ -->
<div id="incoming-call-modal" class="modal">
    <div class="incoming-call-content">
        <img id="caller-avatar" class="caller-avatar" src="">
        <div id="caller-name" class="caller-name"></div>
        <div class="caller-text">邀请你视频通话</div>
        <div class="incoming-call-actions">
            <div class="action-button-wrapper">
                <button id="decline-call-btn" class="call-action-btn decline"></button>
                <span>拒绝</span>
            </div>
            <div class="action-button-wrapper">
                <button id="accept-call-btn" class="call-action-btn accept"></button>
                <span>接听</span>
            </div>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新增结束 ▲▲▲ -->

<!-- ▼▼▼ 请用这段【全新群聊兼容结构】的代码，完整替换你旧的 #video-call-screen ▼▼▼ -->
<div id="video-call-screen" class="screen">
    <!-- 1. 顶部栏 (保持不变) -->
    <div class="video-call-top-bar">
        <span id="call-timer">00:00</span>
    </div>
    
    <!-- 2. 【升级】参与者头像网格区域 -->
    <div class="video-call-avatar-area">
        <div id="participant-avatars-grid">
            <!-- JS会在这里动态生成头像 -->
        </div>
    </div>

    <!-- 3. 对话框区域 (保持不变) -->
    <div id="video-call-main" class="video-call-main">
        <!-- 对话内容会动态生成在这里 -->
    </div>
    
    <!-- 4. 【升级】底部控制栏，现在包含一个“加入”按钮 -->
    <div class="video-call-controls">
        <button id="user-speak-btn" class="control-btn speak-btn"></button>
        <button id="hang-up-btn" class="control-btn hangup-btn"></button>
        <!-- 这个按钮默认隐藏，只在用户“旁观”时显示 -->
        <button id="join-call-btn" class="control-btn join-btn" style="display: none;"></button>
    </div>
</div>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新添加】正在呼叫界面 ▼▼▼ -->
<div id="outgoing-call-screen" class="screen">
    <div class="outgoing-call-content">
        <img id="outgoing-call-avatar" class="caller-avatar" src="">
        <div id="outgoing-call-name" class="caller-name"></div>
        <div class="caller-text">正在呼叫...</div>
        <div class="outgoing-call-actions">
            <button id="cancel-call-btn" class="call-action-btn decline"></button>
            <span>取消</span>
        </div>
    </div>
</div>
<!-- ▲▲▲ 添加结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】通话记录页面 ▼▼▼ -->
<div id="call-history-screen" class="screen">
    <div class="header">
        <span class="back-btn" id="call-history-back-btn">‹</span>
        <span id="call-history-title">通话记录</span>
        <span style="width: 30px;"></span> <!-- 占位符，保持标题居中 -->
    </div>
    <div id="call-history-list" class="list-container" style="padding: 15px; display: flex; flex-direction: column; gap: 15px;">
        <!-- 通话记录卡片将由JS动态生成在这里 -->
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】聊天记录搜索界面 ▼▼▼ -->
<div id="chat-search-screen" class="screen">
    <!-- 1. 界面头部 -->
    <div class="header">
        <span class="back-btn" id="search-back-btn">‹</span>
        <span>查找聊天记录</span>
        <span style="width: 30px;"></span> <!-- 占位符，保持标题居中 -->
    </div>

    <!-- 2. 搜索控制区域 -->
<div id="search-controls" style="padding: 15px; border-bottom: 1px solid var(--border-color);">
    <div class="form-group">
        <label for="keyword-search-input">关键词</label>
        <input type="search" id="keyword-search-input" placeholder="输入关键词搜索..." style="width: 100%; padding: 10px; border-radius: 8px; border: 1px solid var(--border-color);">
    </div>
    <!-- 核心修改：移除了包裹它们的flex div，让它们自然地垂直排列 -->
    <div class="form-group">
        <label for="sender-search-select">人物</label>
        <select id="sender-search-select" style="width: 100%; padding: 10px; border-radius: 8px; border: 1px solid var(--border-color);">
            <!-- 成员选项将由JS动态生成 -->
        </select>
    </div>
    <div class="form-group">
        <label for="date-search-input">日期</label>
        <input type="date" id="date-search-input" style="width: 95%; padding: 8px; border-radius: 8px; border: 1px solid var(--border-color);">
    </div>
</div>

    <!-- 3. 搜索结果列表 -->
    <div id="search-results-list" class="list-container" style="overflow-y: auto; padding-top: 10px;">
        <!-- 搜索结果将由JS动态生成在这里 -->
    </div>
</div>
<!-- ▲▲▲ 新界面HTML结束 ▲▲▲ -->

            <!-- ▼▼▼ 请将下面这整块【全新的HTML代码】粘贴到这里 ▼▼▼ -->
            <div id="lock-screen" class="screen">
                <div id="lock-clock-container">
                    <div id="lock-main-time">12:00</div>
                    <div id="lock-main-date">星期一, 1月1日</div>
                </div>
                <div id="unlock-hint">向上轻扫以解锁</div>
            </div>

            <div id="password-modal-overlay" class="modal">
                <div class="password-modal-content">
                    <p>请输入密码</p>
                    <input type="password" id="password-input-field" maxlength="20">
                    <div class="password-actions">
                        <button id="password-cancel-btn">取消</button>
                        <button id="password-confirm-btn">进入</button>
                    </div>
                </div>
            </div>
            <!-- ▲▲▲ 新HTML代码粘贴结束 ▲▲▲ -->

        </div>
    </div>
  
    <div id="chat-settings-modal" class="modal"><div class="modal-content"><div class="modal-header"><span>聊天设置</span></div><div class="modal-body"><div class="form-group" id="chat-name-group"><label for="chat-name-input">备注名 / 群名</label><input type="text" id="chat-name-input"></div>

    <!-- ▼▼▼ 请将这段新代码粘贴到“备注名”输入框的 form-group 之后 ▼▼▼ -->
    <div class="form-group" id="assign-group-section" style="display: none;"> <!-- 默认隐藏，只对单聊显示 -->
        <label for="assign-group-select">好友分组</label>
        <div style="display: flex; align-items: center; gap: 10px;">
            <select id="assign-group-select" style="flex-grow: 1;">
                <!-- 分组选项将由JS动态生成 -->
            </select>
            <button id="manage-groups-btn" class="form-button-secondary" style="margin-top: 0; padding: 12px;">管理分组</button>
        </div>
    </div>
    <!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->

<div class="form-group" id="my-group-nickname-group"><label for="my-group-nickname-input">我的群昵称</label><input type="text" id="my-group-nickname-input"></div><div class="form-group" id="group-avatar-group"><label>群头像</label><div class="avatar-upload"><img id="group-avatar-preview"><button onclick="document.getElementById('group-avatar-input').click()">上传群头像</button><input type="file" id="group-avatar-input" accept="image/*"></div></div>
            <div class="form-group" id="world-book-link-group">
                <label>关联世界书 (可多选)</label>
                <div class="custom-multiselect">
                    <div class="select-box">
                        <span class="selected-options-text">-- 点击选择 --</span>
                        <span class="arrow-down">▼</span>
                    </div>
                    <div id="world-book-checkboxes-container" class="checkboxes-container">
                    </div>
                </div>
            </div>
            <div class="form-group" id="ai-persona-group"><label for="ai-persona">对方人设 (AI Persona)</label><textarea id="ai-persona" rows="3"></textarea></div><div class="form-group" id="ai-avatar-group"><label>对方头像</label><div class="avatar-upload"><img id="ai-avatar-preview"><button onclick="document.getElementById('ai-avatar-input').click()">上传对方头像</button><button id="manage-ai-avatar-library-btn">管理头像库</button>
<button class="change-frame-btn" data-type="ai">更换头像框</button> <!-- ← 加回这一行 -->
<input type="file" id="ai-avatar-input" accept="image/*"></div></div><div class="form-group" id="my-persona-group"><label for="my-persona">我的人设 (My Persona)</label><textarea id="my-persona" rows="3"></textarea></div><div class="form-group" id="my-avatar-group"><label>我的头像</label><div class="avatar-upload"><img id="my-avatar-preview"><button onclick="document.getElementById('my-avatar-input').click()">上传我的头像</button>
<button class="change-frame-btn" data-type="my">更换头像框</button> <!-- ← 加回这一行 -->
<button id="open-persona-library-btn">预设</button><input type="file" id="my-avatar-input" accept="image/*"></div></div><div class="form-group" id="group-members-group"><label>群成员人设</label><div id="group-members-settings"></div>

    <!-- 【新增】管理成员按钮 -->
    <button id="manage-members-btn" class="form-button form-button-secondary" style="margin-top: 15px;">管理群成员</button></div>
<div class="form-group"><label for="max-memory">上下文记忆条数</label><input type="number" id="max-memory" value="10"></div><div class="form-group"><label>聊天气泡主题 <button id="reset-theme-btn" type="button">重置</button></label><div class="theme-selector"><label><input type="radio" name="theme-select" value="default" id="theme-default"> 默认</label><label><input type="radio" name="theme-select" value="pink_blue"> 粉蓝</label><label><input type="radio" name="theme-select" value="blue_white"> 蓝白</label><label><input type="radio" name="theme-select" value="purple_yellow"> 紫黄</label><label><input type="radio" name="theme-select" value="black_white"> 黑白</label><label><input type="radio" name="theme-select" value="yellow_white"> 黄白</label><label><input type="radio" name="theme-select" value="red_black"> 红黑</label><label><input type="radio" name="theme-select" value="blue_yellow"> 蓝黄</label><label><input type="radio" name="theme-select" value="pink_yellow"> 粉黄</label><label><input type="radio" name="theme-select" value="pink_purple"> 粉紫</label><label><input type="radio" name="theme-select" value="gray_white"> 灰白</label><label><input type="radio" name="theme-select" value="blue_green"> 蓝绿</label><label><input type="radio" name="theme-select" value="pink_white"> 粉白</label><label><input type="radio" name="theme-select" value="pink_black"> 粉黑</label><label><input type="radio" name="theme-select" value="pink_green"> 粉绿</label><label><input type="radio" name="theme-select" value="green_black"> 绿黑</label></div></div>

<!-- ▼▼▼ 请将这段新代码粘贴到“聊天气泡主题”的 form-group 之后 ▼▼▼ -->
<div class="form-group">
    <label for="font-size-slider">聊天字体大小 <span id="font-size-value">13px</span></label>
    <input type="range" id="font-size-slider" min="12" max="20" step="1" value="13" style="width: 100%; margin-top: 8px;">
</div>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】气泡样式预设选择器 ▼▼▼ -->
<div class="form-group">
    <label>气泡样式预设</label>
    <div class="bubble-preset-manager">
        <select id="bubble-style-preset-select" class="form-group select"></select>
        <button id="manage-bubble-presets-btn" class="action-btn">管理</button>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这段新代码粘贴到“聊天字体大小”的 form-group 之后 ▼▼▼ -->
<div class="form-group">
    <label for="custom-css-input">
        自定义气泡样式 (CSS)
        <button id="reset-custom-css-btn" type="button" style="background: none; border: 1px solid #ccc; color: #555; font-size: 12px; padding: 2px 8px; border-radius: 5px; cursor: pointer; margin-left: 10px;">重置</button>
    </label>
    <textarea id="custom-css-input" rows="5" style="width: 100%; margin-top: 8px; font-family: monospace; font-size: 12px; resize: vertical;" placeholder="/* 示例：为“我”的气泡添加渐变背景和阴影 */
.message-bubble.user .content {
  background: linear-gradient(135deg, #a1c4fd, #c2e9fb);
  box-shadow: 0 4px 10px rgba(0,0,0,0.1);
  border-radius: 15px 4px 15px 15px;
}"></textarea>
</div>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这段新代码粘贴到自定义CSS输入框的 form-group 之后 ▼▼▼ -->
<div class="form-group">
    <label>实时预览</label>
    <div id="settings-preview-area">
        <!-- JS会在这里生成预览内容 -->
    </div>
</div>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->

            <div class="form-group">
                <label>聊天背景</label>
                <div class="bg-upload-container">
                    <button type="button" class="form-button-secondary" style="width: auto; padding: 8px 12px; margin-top: 0;" onclick="document.getElementById('bg-input').click()">上传背景图</button>
                    <button type="button" id="remove-bg-btn">移除背景</button>
                </div>
                <img id="bg-preview" class="bg-preview-img">
                <input type="file" id="bg-input" accept="image/*" style="display: none;">
            </div>
<hr style="margin: 25px 0; border: none; border-top: 1px solid #eee;">
<button class="form-button form-button-secondary" id="block-chat-btn" style="background-color: #ff3b30; color: white; border-color: #ff3b30;">拉黑对方</button>
<!-- ▼▼▼ 把这段新代码粘贴到“清空聊天记录”按钮的上方 ▼▼▼ -->
<button class="form-button form-button-secondary" id="find-history-btn">查找聊天记录</button>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->
<button class="form-button form-button-secondary" id="clear-chat-btn">清空聊天记录</button></div><div class="modal-footer"><button class="cancel" id="cancel-chat-settings-btn">取消</button><button class="save" id="save-chat-settings-btn">保存</button></div></div></div>
    
    <div id="persona-library-modal" class="modal"><div class="modal-content"><div class="modal-header"><span>我的人设库</span><button id="add-persona-preset-btn" class="action-button">添加</button></div><div class="modal-body"><div id="persona-library-grid"></div></div><div class="modal-footer"><button class="cancel" id="close-persona-library-btn">关闭</button></div></div></div>
    
    <div id="persona-editor-modal" class="modal"><div class="modal-content"><div class="modal-header"><span id="persona-editor-title">添加人设预设</span></div><div class="modal-body"><div class="form-group"><label>预设头像</label><div class="avatar-upload"><img id="preset-avatar-preview"><button onclick="document.getElementById('preset-avatar-input').click()">上传头像</button>
    <button class="change-frame-btn" data-type="member">更换头像框</button> <!-- ← 加回这一行 -->
<input type="file" id="preset-avatar-input" accept="image/*"></div></div><div class="form-group"><label for="preset-persona-input">预设人设</label><textarea id="preset-persona-input" rows="4" placeholder="在此输入这个人设的详细设定..."></textarea></div></div><div class="modal-footer"><button class="cancel" id="cancel-persona-editor-btn">取消</button><button class="save" id="save-persona-preset-btn">保存</button></div></div></div>

    <div id="member-settings-modal" class="modal"><div class="modal-content"><div class="modal-header"><span>编辑群成员</span></div><div class="modal-body">
    <div class="form-group"><label for="member-name-input">名字</label><input type="text" id="member-name-input"></div>
    <div class="form-group"><label for="member-persona-input">人设</label><textarea id="member-persona-input" rows="4"></textarea></div>
    <div class="form-group"><label>头像</label><div class="avatar-upload"><img id="member-avatar-preview"><button onclick="document.getElementById('member-avatar-input').click()">上传头像</button><input type="file" id="member-avatar-input" accept="image/*"></div></div>
    </div><div class="modal-footer"><button class="cancel" id="cancel-member-settings-btn">取消</button><button class="save" id="save-member-settings-btn">保存</button></div></div></div>
    
    <div id="custom-modal-overlay">
        <div id="custom-modal">
            <div class="custom-modal-header" id="custom-modal-title"></div>
            <div class="custom-modal-body" id="custom-modal-body"></div>
            <div class="custom-modal-footer">
                <button id="custom-modal-cancel">取消</button>
                <button id="custom-modal-confirm" class="confirm-btn">确定</button>
            </div>
        </div>
    </div>
    
    <div id="preset-actions-modal" class="modal">
        <div id="custom-modal" style="width: 250px;">
            <div class="custom-modal-footer">
                <button id="preset-action-edit">编辑预设</button>
                <button id="preset-action-delete" class="btn-danger">删除预设</button>
                <button id="preset-action-cancel" style="margin-top: 8px; border-radius: 8px; background-color: #f0f0f0;">取消</button>
            </div>
        </div>
    </div>

    <div id="transfer-modal">
        <div class="transfer-content">
            <div class="transfer-header">给Ta一个惊喜！</div>
            <div class="transfer-input-group">
                <label for="transfer-amount">转账金额</label>
                <input type="number" id="transfer-amount" placeholder="0.00" min="0" max="9999999999999" step="0.01">
            </div>
            <div class="transfer-input-group">
                <label for="transfer-note">备注 (可选)</label>
                <input type="text" id="transfer-note" placeholder="留下你的小心思~" maxlength="20">
            </div>
            <div class="transfer-actions">
                <button id="transfer-cancel-btn">取消</button>
                <button id="transfer-confirm-btn">确认转账</button>
            </div>
        </div>
    </div>

 <div id="battery-alert-modal">
    <div class="battery-alert-content">
        <img id="battery-alert-image" src="">
        <p id="battery-alert-text"></p>
    </div>
</div>

    <audio id="audio-player" style="display:none;"></audio>
<audio id="ringtone-player" loop></audio>

<!-- ▼▼▼ 用下面这段【完整】的模态框代码，替换掉你现有的 id="create-post-modal" 的整个 div ▼▼▼ -->
<div id="create-post-modal" class="modal">
    <div class="modal-content" style="height: auto; max-height: 90%;">
        <div class="modal-header">
            <span>发布动态</span>
        </div>
        <div class="modal-body">
            <!-- 公开文字输入区 -->
            <div class="form-group">
                <textarea id="post-public-text" rows="3" placeholder="分享新鲜事...（非必填的公开文字）"></textarea>
            </div>

            <!-- === 模式切换开关 (新增) === -->
            <div class="post-mode-switcher">
                <button id="switch-to-image-mode" class="mode-btn active">上传图片</button>
                <button id="switch-to-text-image-mode" class="mode-btn">使用文字图</button>
            </div>

<!-- ▼▼▼ 【修正后】的可见范围设置 ▼▼▼ -->
<div class="form-group">
    <label>可见范围</label>
    <div id="post-visibility-options" style="display: flex; gap: 15px; margin-bottom: 10px;">
        <label><input type="radio" name="visibility" value="public" checked> 公开</label>

        <label><input type="radio" name="visibility" value="include"> 指定分组可见</label>
    </div>
    <div id="post-visibility-groups" style="display: none; max-height: 120px; overflow-y: auto; background: #f9f9f9; padding: 10px; border-radius: 8px;">
        <!-- 分组多选框将由JS动态生成 -->
    </div>
</div>
<!-- ▲▲▲ 修正结束 ▲▲▲ -->

            <!-- === 图片模式区域 === -->
            <div id="image-mode-content" class="post-mode-content active">
                <div class="form-group">
<div id="post-image-preview-container" class="post-image-preview-container">
                        <img id="post-image-preview" src="" alt="图片预览">
                        <button id="post-remove-image-btn">×</button>
                    </div>
                    <div class="post-image-upload-options">
                        <button id="post-upload-local-btn" class="form-button-secondary">本地上传</button>
                        <button id="post-use-url-btn" class="form-button-secondary">网络URL</button>
                        <input type="file" id="post-local-image-input" accept="image/*" hidden>
                    </div>
                </div>
                <div id="post-image-desc-group" class="form-group" style="display: none;">
                    <label>图片描述 (必填，给AI看)</label>
                    <input type="text" id="post-image-description" placeholder="简单描述图片内容，帮助AI理解">
                </div>
            </div>

            <!-- === 文字图模式区域 (新增) === -->
            <div id="text-image-mode-content" class="post-mode-content">
                <div class="form-group">
                    <label>文字图 (给AI理解用的描述，点击图片后可见)</label>
                    <textarea id="post-hidden-text" rows="4" placeholder="在这里写下图片描述..."></textarea>
                </div>
            </div>

        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-create-post-btn">取消</button>
            <button class="save" id="confirm-create-post-btn">发布</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这个新的模态框HTML粘贴到所有其他模态框之后 ▼▼▼ -->
<div id="group-management-modal" class="modal">
    <div class="modal-content" style="height: 60%;">
        <div class="modal-header">
            <span>管理好友分组</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label>新建分组</label>
                <div style="display: flex; gap: 10px;">
                    <input type="text" id="new-group-name-input" placeholder="输入分组名..." style="flex-grow: 1;">
                    <button id="add-new-group-btn" class="form-button" style="width: auto; margin-top: 0; padding: 0 15px;">添加</button>
                </div>
            </div>
            <hr style="opacity: 0.2;">
            <div id="existing-groups-list" style="display: flex; flex-direction: column; gap: 10px;">
                <!-- 分组列表将由JS动态生成 -->
            </div>
        </div>
        <div class="modal-footer">
            <button class="save" id="close-group-manager-btn" style="width: 100%;">完成</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新代码粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这段新HTML粘贴到所有模态框的末尾 ▼▼▼ -->
<div id="message-actions-modal" class="modal">
    <div id="custom-modal" style="width: 250px;">
        <div class="custom-modal-footer">
            <!-- 新的操作按钮 -->
            <button id="edit-message-btn">编辑消息</button>
            <button id="copy-message-btn">复制文本</button>
            <button id="recall-message-btn">撤回</button>
<button id="quote-message-btn">引用</button>
            <button id="select-message-btn">进入多选</button>
            <!-- 取消按钮 -->
            <button id="cancel-message-action-btn" style="margin-top: 8px; border-radius: 8px; background-color: #f0f0f0;">取消</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 请将这段新HTML粘贴到所有模态框的末尾 ▼▼▼ -->
<div id="post-actions-modal" class="modal">
    <div id="custom-modal" style="width: 250px;">
        <div class="custom-modal-footer">
            <button id="edit-post-btn">编辑动态</button>
            <button id="copy-post-btn">复制内容</button>           
            <button id="cancel-post-action-btn">取消</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】可视化消息编辑器模态框 ▼▼▼ -->
<div id="message-editor-modal" class="modal">
    <div class="modal-content" style="height: 75%;">
        <div class="modal-header">
            <span>编辑与拆分消息</span>
        </div>
        <div class="modal-body" id="message-editor-body">
            <!-- 编辑器容器，JS会在这里动态生成文本框 -->
            <div id="message-editor-container"></div>
            <!-- 添加新消息的按钮 -->
            <button id="add-message-editor-block-btn" class="form-button form-button-secondary" style="margin-top: 15px;">
                [+] 添加下一条消息
            </button>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-advanced-editor-btn">取消</button>
            <button class="save" id="save-advanced-editor-btn">保存更改</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】外卖请求模态框 ▼▼▼ -->
<div id="waimai-request-modal" class="modal">
    <div class="modal-content" style="width: 290px;">
        <div class="modal-header">
            <span>发起外卖代付</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label for="waimai-product-info">商品信息</label>
                <input type="text" id="waimai-product-info" placeholder="例如：一杯杨枝甘露">
            </div>
            <div class="form-group">
                <label for="waimai-amount">代付金额 (元)</label>
                <input type="number" id="waimai-amount" placeholder="例如：21" min="0" step="0.01">
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="waimai-cancel-btn">取消</button>
            <button class="save" id="waimai-confirm-btn">发起请求</button>
        </div>
    </div>
</div>

<!-- ▼▼▼ 【全新】新建约定/倒计时模态框 ▼▼▼ -->
<div id="create-countdown-modal" class="modal">
    <div class="modal-content" style="height: auto;">
        <div class="modal-header">
            <span>新建约定</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label for="countdown-title-input">约定标题</label>
                <input type="text" id="countdown-title-input" placeholder="例如：我的生日">
            </div>
            <div class="form-group">
                <label for="countdown-date-input">约定日期与时间</label>
                <input type="datetime-local" id="countdown-date-input"style="width: 95%; padding: 8px; border-radius: 8px; border: 1px solid var(--border-color);">
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-create-countdown-btn">取消</button>
            <button class="save" id="confirm-create-countdown-btn">保存约定</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】发红包模态框 ▼▼▼ -->
<div id="red-packet-modal" class="modal">
    <div class="modal-content" style="width: 300px; height: auto;">
        <div class="modal-header">
            <span>发红包</span>
        </div>
        <div class="modal-body" style="padding: 0;">
<!-- 1. 页签切换 -->
<div class="frame-tabs">
    <div id="rp-tab-group" class="frame-tab active">拼手气红包</div>
    <div id="rp-tab-direct" class="frame-tab">专属红包</div>
</div>

            <!-- 2. 拼手气红包内容区 -->
            <div id="rp-content-group" class="frame-content" style="padding: 20px 15px;">
                <div class="form-group">
                    <label>总金额 (元)</label>
                    <input type="number" id="rp-group-amount" placeholder="0.00">
                </div>
                <div class="form-group">
                    <label>红包个数</label>
                    <input type="number" id="rp-group-count" placeholder="填写红包个数">
                </div>
                <div class="form-group">
                    <label>祝福语</label>
                    <input type="text" id="rp-group-greeting" placeholder="恭喜发财，大吉大利！">
                </div>
                <p id="rp-group-total" style="text-align: center; font-size: 24px; font-weight: bold; margin: 10px 0;">¥ 0.00</p>
                <button id="send-group-packet-btn" class="form-button">塞钱进红包</button>
            </div>

            <!-- 3. 专属红包内容区 -->
            <div id="rp-content-direct" class="frame-content" style="display: none; padding: 20px 15px;">
                <div class="form-group">
                    <label>发送给</label>
                    <select id="rp-direct-receiver"></select>
                </div>
                <div class="form-group">
                    <label>金额 (元)</label>
                    <input type="number" id="rp-direct-amount" placeholder="0.00">
                </div>
                <div class="form-group">
                    <label>祝福语</label>
                    <input type="text" id="rp-direct-greeting" placeholder="恭喜发财，大吉大利！">
                </div>
                 <p id="rp-direct-total" style="text-align: center; font-size: 24px; font-weight: bold; margin: 10px 0;">¥ 0.00</p>
                <button id="send-direct-packet-btn" class="form-button">塞钱进红包</button>
            </div>
        </div>
        <div class="modal-footer" style="justify-content: center;">
             <button class="cancel" id="cancel-red-packet-btn" style="width: 100%;">取消</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】红包详情模态框 ▼▼▼ -->
<div id="red-packet-details-modal" class="modal">
    <div class="modal-content" style="width: 280px; height: auto; background-color: #f7f7f7;">
        <div class="modal-header" style="background-color: #F96259; color: white; border-bottom: none; padding-bottom: 5px;">
            <div style="text-align: center; width: 100%;">
                <div id="rp-details-sender" style="font-size: 16px;"></div>
                <div style="font-size: 13px; opacity: 0.8;">的红包</div>
            </div>
        </div>
        <div class="modal-body" style="padding: 15px;">
            <p id="rp-details-greeting" style="text-align: center; font-size: 20px; color: #333; margin: 0 0 20px 0;"></p>
            <div id="rp-details-my-amount" style="text-align: center; display: none; margin-bottom: 20px;">
                <span style="font-size: 40px; font-weight: bold; color: #E44D44;">0.00</span>
                <span style="font-size: 18px; color: #E44D44;">元</span>
            </div>
            <div id="rp-details-summary" style="font-size: 13px; color: #8a8a8a; border-top: 1px solid #e0e0e0; padding-top: 10px;"></div>
            <div id="rp-details-list" style="max-height: 150px; overflow-y: auto; margin-top: 10px;">
                <!-- 领取详情将由JS动态生成在这里 -->
            </div>
        </div>
        <div class="modal-footer">
            <button class="save" id="close-rp-details-btn" style="width: 100%;">关闭</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->
<!-- ▼▼▼ 【全新】创建投票模态框 ▼▼▼ -->
<div id="create-poll-modal" class="modal">
    <div class="modal-content" style="width: 300px; height: auto;">
        <div class="modal-header">
            <span>发起投票</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label for="poll-question-input">投票问题</label>
                <textarea id="poll-question-input" rows="2" placeholder="例如：今晚我们看什么电影？"></textarea>
            </div>
            <div class="form-group">
                <label>投票选项 (至少2项)</label>
                <div id="poll-options-container" style="display: flex; flex-direction: column; gap: 8px;">
                    <!-- 投票选项将由JS动态生成在这里 -->
                </div>
                <button id="add-poll-option-btn" class="form-button form-button-secondary" style="margin-top: 12px;">+ 添加选项</button>
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-create-poll-btn">取消</button>
            <button class="save" id="confirm-create-poll-btn">发起投票</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】AI头像库管理模态框 ▼▼▼ -->
<div id="ai-avatar-library-modal" class="modal">
    <div class="modal-content" style="height: 70%;">
        <div class="modal-header">
            <span id="ai-avatar-library-title">对方的头像库</span>
            <button id="add-ai-avatar-btn" class="action-button">添加</button>
        </div>
        <div class="modal-body" style="padding: 15px;">
            <div id="ai-avatar-library-grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 15px;">
                <!-- 头像库内容将由JS动态生成 -->
            </div>
        </div>
        <div class="modal-footer">
            <button class="save" id="close-ai-avatar-library-btn" style="width: 100%;">关闭</button>
        </div>
    </div>
</div>

<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】用户分享链接模态框 ▼▼▼ -->
<div id="share-link-modal" class="modal">
    <div class="modal-content" style="width: 300px; height: auto;">
        <div class="modal-header">
            <span>分享链接</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label for="link-title-input">标题</label>
                <input type="text" id="link-title-input" placeholder="输入文章或链接的标题">
            </div>
            <div class="form-group">
                <label for="link-description-input">摘要 (可选)</label>
                <textarea id="link-description-input" rows="2" placeholder="简单描述一下链接内容"></textarea>
            </div>
            <div class="form-group">
                <label for="link-source-input">来源名称 (可选)</label>
                <input type="text" id="link-source-input" placeholder="例如：知乎日报、B站">
            </div>
            <div class="form-group">
                <label for="link-content-input">完整内容 (可选，用于浏览器内显示)</label>
                <textarea id="link-content-input" rows="4" placeholder="粘贴或输入完整的文章内容"></textarea>
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-share-link-btn">取消</button>
            <button class="save" id="confirm-share-link-btn">分享</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】精致版转账操作弹窗 ▼▼▼ -->
<div id="transfer-actions-modal" class="modal">
    <div class="transfer-actions-content">
        <div class="transfer-actions-header">请选择操作</div>
        <div class="transfer-actions-body">
            <p>你收到了来自 <strong id="transfer-sender-name"></strong> 的一笔转账。</p>
        </div>
        <div class="transfer-actions-footer">
            <button id="transfer-action-decline" class="action-btn decline">残忍拒绝</button>
            <button id="transfer-action-accept" class="action-btn accept">开心收下</button>
        </div>
        <button id="transfer-action-cancel" class="cancel-btn">×</button>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】通话记录详情模态框 ▼▼▼ -->
<div id="call-transcript-modal" class="modal">
    <div class="modal-content" style="height: 70%;">
        <div class="modal-header">
            <span id="transcript-modal-title">通话详情</span>
        </div>
        <div class="modal-body" id="transcript-modal-body" style="background-color: #f0f2f5;">
            <!-- 通话文字记录将由JS动态生成在这里 -->
        </div>
        <div class="modal-footer">
            <button class="cancel" id="delete-transcript-btn" style="background-color: #ff3b30; color: white; border-color: #ff3b30;">删除记录</button>
            <button class="save" id="close-transcript-modal-btn" style="width: 100%;">关闭</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】分享目标选择器模态框 ▼▼▼ -->
<div id="share-target-modal" class="modal">
    <div class="modal-content" style="height: 70%;">
        <div class="modal-header">
            <span>分享到...</span>
        </div>
        <div class="modal-body" id="share-target-list" style="padding: 0;">
            <!-- 聊天列表将由JS动态生成在这里 -->
        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-share-target-btn">取消</button>
            <button class="save" id="confirm-share-target-btn">确认分享</button>
        </div>
    </div>
</div>

<!-- ▼▼▼ 【全新】分享记录查看器模态框 ▼▼▼ -->
<div id="shared-history-viewer-modal" class="modal">
    <div class="modal-content" style="height: 80%;">
        <div class="modal-header">
            <span id="shared-history-viewer-title">聊天记录</span>
        </div>
        <div class="modal-body" id="shared-history-viewer-content" style="background-color: #f0f2f5;">
            <!-- 分享的聊天记录气泡将由JS动态生成在这里 -->
        </div>
        <div class="modal-footer">
            <button class="save" id="close-shared-history-viewer-btn" style="width:100%;">关闭</button>
        </div>
    </div>
</div>

<!-- ▼▼▼ 【全新】世界书分类管理模态框 ▼▼▼ -->
<div id="world-book-category-manager-modal" class="modal">
    <div class="modal-content" style="height: 60%;">
        <div class="modal-header">
            <span>管理世界书分类</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label>新建分类</label>
                <div style="display: flex; gap: 10px;">
                    <input type="text" id="new-category-name-input" placeholder="输入分类名..." style="flex-grow: 1;">
                    <button id="add-new-category-btn" class="form-button" style="width: auto; margin-top: 0; padding: 0 15px;">添加</button>
                </div>
            </div>
            <hr style="opacity: 0.2;">
            <div id="existing-categories-list" style="display: flex; flex-direction: column; gap: 10px;">
                <!-- 分类列表将由JS动态生成 -->
            </div>
        </div>
        <div class="modal-footer">
            <button class="save" id="close-category-manager-btn" style="width: 100%;">完成</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】自定义头像框管理模态框 ▼▼▼ -->
<div id="custom-frame-manager-modal" class="modal">
    <div class="modal-content" style="height: 70%;">
        <div class="modal-header">
            <span>管理我的头像框</span>
            <button id="upload-custom-frame-btn" class="action-button">上传</button>
        </div>
        <div class="modal-body" style="padding: 15px;">
            <div id="custom-frame-grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 15px;">
                <!-- 自定义头像框将由JS动态生成在这里 -->
            </div>
        </div>
        <div class="modal-footer">
            <button class="save" id="close-frame-manager-btn" style="width: 100%;">关闭</button>
        </div>
    </div>
</div>
<input type="file" id="custom-frame-upload-input" accept="image/png, image/gif" hidden multiple>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<!-- ▼▼▼ 【已修改】头像框选择模态框 ▼▼▼ -->
<div id="avatar-frame-modal" class="modal">
    <div class="modal-content" style="height: 70%;">
        <div class="modal-header">
            <span>选择头像框</span>
            <!-- 【核心修改】我们在这里加一个“管理”按钮 -->
            <button id="manage-custom-frames-btn" class="action-button">管理</button>
        </div>
        <div class="modal-body" style="padding: 15px;">
            <!-- 【核心修改】不再需要Tabs，只有一个Grid -->
            <div id="avatar-frame-grid" class="frame-grid">
                <!-- 头像框选项（包括“无”和自定义的）会在这里生成 -->
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="cancel-frame-settings-btn">取消</button>
            <button class="save" id="save-frame-settings-btn">保存</button>
        </div>
    </div>
</div>

<!-- ▼▼▼ 用这整块【全新设计】的代码，替换旧的 id="send-location-modal" ▼▼▼ -->
<div id="send-location-modal" class="modal">
    <div class="modal-content" style="width: 300px; height: auto; max-height: 80%;">
        <div class="modal-header">
            <span>发送定位与轨迹</span>
        </div>
        <div class="modal-body" style="padding-bottom: 5px;">
            <!-- 核心信息 -->
            <div class="form-group">
                <label for="user-location-input">我的位置 (起点)</label>
                <input type="text" id="user-location-input" placeholder="例如：市中心的咖啡馆">
            </div>
            <div class="form-group">
                <label for="ai-location-input">Ta的位置 (终点)</label>
                <input type="text" id="ai-location-input" placeholder="例如：海边的灯塔">
            </div>
            <div class="form-group">
                <label for="distance-input">相距</label>
                <input type="text" id="distance-input" placeholder="例如：约5公里 (必填)">
            </div>
            
            <hr style="opacity: 0.2;">

            <!-- 【全新】行动轨迹输入区 -->
            <div class="form-group">
                <label>行动轨迹 (可选，按顺序填写)</label>
                <div id="trajectory-points-container" style="display: flex; flex-direction: column; gap: 8px;">
                    <!-- JS会在这里动态添加输入框 -->
                </div>
                <button id="add-trajectory-point-btn" class="form-button form-button-secondary" style="margin-top: 12px;">+ 添加途经点</button>
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="location-cancel-btn">取消</button>
            <button class="save" id="location-confirm-btn">发送</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 替换结束 ▲▲▲ -->

<!-- ▼▼▼ 【全新】粘贴悬浮歌词栏设置模态框 ▼▼▼ -->
<div id="lyrics-settings-modal" class="modal">
    <div class="modal-content" style="height: auto;">
        <div class="modal-header">
            <span>悬浮歌词设置</span>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label for="lyrics-font-size-slider">字体大小: <span id="lyrics-font-size-value">14px</span></label>
                <input type="range" id="lyrics-font-size-slider" min="12" max="24" value="14" style="width: 100%;">
            </div>
            <div class="form-group">
                <label for="lyrics-bg-opacity-slider">背景不透明度: <span id="lyrics-bg-opacity-value">0%</span></label>
                <input type="range" id="lyrics-bg-opacity-slider" min="0" max="100" value="0" style="width: 100%;">
            </div>
            <div class="form-group">
                <label for="lyrics-font-color-picker">字体颜色</label>
                <input type="color" id="lyrics-font-color-picker" value="#FFFFFF" style="width: 100%; height: 40px;">
            </div>
        </div>
        <div class="modal-footer">
            <button class="cancel" id="reset-lyrics-settings-btn">恢复默认</button>
            <button class="save" id="close-lyrics-settings-btn">完成</button>
        </div>
    </div>
</div>
<!-- ▲▲▲ 新HTML粘贴结束 ▲▲▲ -->

<script>

const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models'
    // gemini如果是多个密钥, 那么随机获取一个
    function getRandomValue(str) {
        // 检查字符串是否包含逗号
        if (str.includes(',')) {
            // 用逗号分隔字符串并移除多余空格
            const arr = str.split(',').map(item => item.trim());
            // 生成随机索引 (0 到 arr.length-1)
            const randomIndex = Math.floor(Math.random() * arr.length);
            // 返回随机元素
            return arr[randomIndex];
        }
        // 没有逗号则直接返回原字符串
        return str;
    }
    function isImage(text,content) {
        let currentImageData = content.image_url.url
        // 提取Base64数据（去掉前缀）
        const base64Data = currentImageData.split(',')[1];
        // 根据图片类型获取MIME类型
        const mimeType = currentImageData.match(/^data:(.*);base64/)[1];
        return [
            {text: `${text.text}用户向你发送了一张图片`},
            {
                inline_data: {
                    mime_type: mimeType,
                    data: base64Data
                }
            }
        ]
    }

   function extractArray(text) {
        // 正则表达式模式：匹配开头的时间戳部分和后续的JSON数组
        const pattern = /^\(Timestamp: (\d+)\)(.*)$/s;
        const match = text.match(pattern);

        if (match) {
            const timestampPart = `(Timestamp: ${match[1]}) `;
            const jsonPart = match[2].trim();

            try {
                // 尝试解析JSON部分
                const parsedJson = JSON.parse(jsonPart);
                // 验证解析结果是否为数组
                if (Array.isArray(parsedJson)) {
                    return [timestampPart, parsedJson[0]];
                }
            } catch (error) {
                // 解析失败，返回原始文本
            }
        }

        // 不匹配格式或解析失败时返回原值
        return text;
    }
    function transformChatData(item) {
        let type = {
            send_and_recall:'撤回了消息',
            update_status:'更新了状态',
            change_music:'切换了歌曲',
            create_memory:'记录了回忆',
            create_countdown:'创建了约定/倒计时',
            text:'发送了文本',
            sticker:'发送了表情',
            ai_image:'发送了图片',
            voice_message:'发送了语音',
            transfer:'发起了转账',
            waimai_request:'发起了外卖请求',
            waimai_response:{
                paid:'回应了外卖-同意',
                rejected:'回应了外卖-拒绝'
            },
            video_call_request:'发起了视频通话',
            video_call_response:{
                accept:'回应了视频通话-接受',
                reject:'回应了视频通话-拒绝'
            },
            qzone_post:{
                shuoshuo:'发布了说说',
                text_image:'发布了文字图'
            },
            qzone_comment:'评论了动态',
            qzone_like:'点赞了动态',
            pat_user:'拍一拍了用户',
            block_user:'拉黑了用户',
            friend_request_response:'回应了好友申请',
            change_avatar:'更换了头像',
            share_link:'分享了链接',
            accept_transfer:'回应了转账-接受',
            decline_transfer:'回应了转账-拒绝/退款',
            quote_reply:'引用了回复',
            text:'',
        }
        let res = extractArray(item.content)

        if(Array.isArray(res)){
            let obj = res[1]
            let itemType = obj.type;
            let time = res[0]
            let text = type[itemType];
            if(text){
                if(itemType === 'sticker'){
                    return [{text:`${time}[${text}] 含义是:${obj.meaning}`}]
                }else if(itemType === 'send_and_recall'){
                    return [{text:`${time}[${text}] ${obj.content}`}]
                }else if(itemType === 'update_status'){
                    return [{text:`${time}[${text}] ${obj.status_text}(${obj.is_busy ? '忙碌/离开' : '空闲'})`}]
                }else if(itemType === 'change_music'){
                    return [{text:`${time}[${text}] ${obj.change_music}, 歌名是:${obj.song_name}`}]
                }else if(itemType === 'create_memory'){
                    return [{text:`${time}[${text}] ${obj.description}`}]
                }else if(itemType === 'create_countdown'){
                    return [{text:`${time}[${text}] ${obj.title}(${obj.date})`}]
                }else if(itemType === 'ai_image'){
                    return [{text:`${time}[${text}] 图片描述是:${obj.description}`}]
                }else if(itemType === 'voice_message'){
                    return [{text:`${time}[${text}] ${obj.content}`}]
                }else if(itemType === 'transfer'){
                    return [{text:`${time}[${text}] 金额是:${obj.amount} 备注是:${obj.amount}`}]
                }else if(itemType === 'waimai_request'){
                    return [{text:`${time}[${text}] 金额是:${obj.amount} 商品是:${obj.productInfo}`}]
                }else if(itemType === 'waimai_response'){
                    return [{text:`${time}[${text[obj.status]}] ${obj.status === 'paid' ? '同意' : '拒绝'}`}]
                }else if(itemType === 'video_call_request'){
                    return [{text:`${time}[${text}]`}]
                }}else if(itemType === 'video_call_request'){
                    return [{text:`${time}[${text[obj.decision]}] ${obj.decision === 'accept' ? '同意' : '拒绝'}`}]
                }else if(itemType === 'qzone_post'){
                    return [{text:`${time}[${text[obj.postType]}] ${obj.postType === 'shuoshuo' ? `${obj.content}` : `图片描述是:${obj.hiddenContent} ${obj.publicText ? `文案是: ${obj.publicText}` : ''}`}`}]
                }else if(itemType === 'qzone_comment'){
                    return [{text:`${time}[${text}] 评论的id是: ${obj.postId} 评论的内容是: ${obj.commentText}`}]
                }else if(itemType === 'qzone_like'){
                    return [{text:`${time}[${text}] 点赞的id是: ${obj.postId}`}]
                }else if(itemType === 'pat_user'){
                    return [{text:`${time}[${text}] ${obj.suffix ? obj.suffix  : ''}`}]
                }else if(itemType === 'block_user'){
                    return [{text:`${time}[${text}]`}]
                }else if(itemType === 'friend_request_response'){
                    return [{text:`${time}[${text}] 结果是:${obj.decision === 'accept' ? '同意' : '拒绝'}`}]
                }else if(itemType === 'change_avatar'){
                    return [{text:`${time}[${text}] 头像名是:${obj.name}`}]
                }else if(itemType === 'share_link'){
                    return [{text:`${time}[${text}] 文章标题是:${obj.title}  文章摘要是:${obj.description} 来源网站名是:${obj.source_name} 文章正文是:${obj.content}`}]
                }else if(itemType === 'accept_transfer'){
                    return [{text:`${time}[${text}]`}]
                }else if(itemType === 'accept_transfer'){
                    return [{text:`${time}[${text}]`}]
                }else if(itemType === 'quote_reply'){
                    return [{text:`${time}[${text}] 引用的内容是:${obj.reply_content}`}]
                }else if(itemType === 'text'){
                    return [{text:`${time}${obj.content}`}]
                }
            }

if(Array.isArray(res) && res.length > 1) {
	res = `${res[0]}${res[1].content}`
}

        return [{text:res}]
    }

    function toGeminiRequestData(model, apiKey, systemInstruction, messagesForDecision,isGemini) {

	if(!isGemini){
		return undefined
	}

        // 【核心修正】在这里，我们将 'system' 角色也映射为 'user'

        let roleType = {
            user: 'user',
            assistant: 'model',
            system: 'user' // <--- 新增这一行
        }
        return {
            url: `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${getRandomValue(apiKey)}`,
            data: {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    contents: messagesForDecision.map((item) => {
                        let  includesImages = false;
                        if(Array.isArray(item.content) && item.content.length === 2){
                              includesImages =  item.content.some((sub)=>{
                                return sub.type === 'image_url' && sub.image_url.url
                            })
                        }
                        return {
                            role: roleType[item.role], // 现在 'system' 会被正确转换为 'user'
                            parts: includesImages ? isImage(item.content[0],item.content[1]) : transformChatData(item)
                        }
                    }),
                    generationConfig: {
                        temperature: 0.8,
                    },
                    "systemInstruction": {
                        "parts": [{
                            "text": systemInstruction
                        }]
                    }
                })
            }
        }
    }
    document.addEventListener('DOMContentLoaded', () => {

        // ===================================================================
        // 1. 所有变量和常量定义
        // ===================================================================
        const db = new Dexie('GeminiChatDB');
// ▼▼▼ 在这里添加您的 API 站点黑名单 ▼▼▼
const BLOCKED_API_SITES = [
    'api.pisces.ink',
    'aiapi.qzz.io'
];
// ▲▲▲ 黑名单定义结束 ▲▲▲
        // --- 已修正 ---
        let state = { chats: {}, activeChatId: null, globalSettings: {}, apiConfig: {}, userStickers: [], worldBooks: [], personaPresets: [], qzoneSettings: {}, activeAlbumId: null };
        // --- 修正结束 ---

        let isLocked = false; // <-- 在这里添加这行新代码
        let newLockscreenWallpaperBase64 = null; // <-- 在这里添加这行新代码

let musicState = { 
    isActive: false, 
    activeChatId: null, 
    isPlaying: false, 
    playlist: [], 
    currentIndex: -1, 
    playMode: 'order', 
    totalElapsedTime: 0, 
    timerId: null,
    // 【新增】歌词相关状态
    parsedLyrics: [],      // 当前歌曲解析后的歌词数组
    currentLyricIndex: -1  // 当前高亮的歌词行索引
};

let lyricsBarSettings = {
    fontSize: 14,
    bgOpacity: 0,
    fontColor: '#FFFFFF',
    showOnClose: true // 【问题4需要】默认开启
};

        const audioPlayer = document.getElementById('audio-player');
        let newWallpaperBase64 = null;
        let isSelectionMode = false;
        let selectedMessages = new Set();
        let editingMemberId = null;
        let editingWorldBookId = null;
        let editingPersonaPresetId = null;

let activeCharacterPhoneId = null;

let waimaiTimers = {}; // 用于存储外卖倒计时

let activeMessageTimestamp = null;
let currentReplyContext = null; // <--- 新增这行，用来存储当前正在引用的消息信息
let activePostId = null; // <-- 新增：用于存储当前操作的动态ID

        let photoViewerState = {
            isOpen: false,
            photos: [], // 存储当前相册的所有照片URL
            currentIndex: -1, // 当前正在查看的照片索引
        };

        let unreadPostsCount = 0;

        let isFavoritesSelectionMode = false;
        let selectedFavorites = new Set()

let simulationIntervalId = null;

let currentFrameSelection = { type: null, url: '', target: null }; 

        const defaultAvatar = 'https://i.postimg.cc/PxZrFFFL/o-o-1.jpg';
        const defaultMyGroupAvatar = 'https://i.postimg.cc/cLPP10Vm/4.jpg';
        const defaultGroupMemberAvatar = 'https://i.postimg.cc/VkQfgzGJ/1.jpg';
        const defaultGroupAvatar = 'https://i.postimg.cc/gc3QYCDy/1-NINE7-Five.jpg';
        let notificationTimeout;

const THEME_CSS_TEMPLATE = `
/* 
  EPhone 美化代码模板
  使用方法: 
  1. 修改下面的颜色代码或图片URL。
  2. 不需要修改的部分可以删除或保持不变。
  3. 颜色代码格式为 #RRGGBB (例如 #FFFFFF 是白色)。
  4. 图片URL需要是网络直链。
*/

/* === 1. 手机壳与刘海颜色 === */
#phone-frame {
  background-color: #f0f0f0; /* 手机壳颜色 */
}
.notch {
  background-color: #1a1a1a; /* 顶部“刘海”颜色 */
}
        #clock-container {  color: white;  }


/* === 1.5. 全局主题色 (重要！) === */
/* 这个颜色决定了大部分按钮、链接和高亮文本的颜色。*/
:root {
  --accent-color: #007bff; /* 默认是蓝色 */
}

/* === 2. 聊天界面顶部和底部的图片按钮替换 === */
/* “一起听”按钮 (正常状态) */
#listen-together-btn img[src*="8kYShvrJ/90-UI-2.png"] {
  content: url('在这里粘贴你的“正常状态”图片URL');
}
/* “一起听”按钮 (播放中状态) */
#listen-together-btn img[src*="D0pq6qS2/E30078-DC-8-B99-4-C01-AFDA-74728-DBF7-BEA.png"] {
  content: url('在这里粘贴你的“播放中”图片URL');
}
/* “聊天设置”按钮 */
#chat-settings-btn img {
  content: url('https://i.postimg.cc/bvPq64cv/CCA834-BA-5-A90-408-D-94-FA-7-EE156-B6-A765.png');
}
/* “触发API回复”按钮 */
#wait-reply-btn img {
  content: url('https://i.postimg.cc/q72zq80N/ECE92-BBC-BE57-48-E9-BB2-C-345-B6019-C4-B2.png');
}
/* “发送”按钮 (设为图片形式) */
#send-btn {
  background-image: url('在这里粘贴你的发送按钮图片URL');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  width: 50px; /* 根据你的图片调整宽度 */
}

/* “重新生成回复”按钮 */
#reroll-btn {
    background-color: rgba(255, 255, 255, 0.6);
    color: var(--text-primary); /* 使用全局主题的主文本颜色 */
}

/* === 3. 顶部栏与底部栏颜色 === */
.header, .qzone-header {
  background-color: rgba(240, 240, 240, 0.8); /* 顶部栏背景色 (带一点透明) */
  color: #333333; /* 顶部栏文字颜色 */
}
#chat-list-bottom-nav {
  background-color: rgba(245, 245, 245, 0.85); /* 底部导航栏背景色 */
}
.nav-item {
  color: #8a8a8a; /* 底部导航栏未选中项的颜色 */
}
.nav-item.active {
  color: #007bff; /* 底部导航栏选中项的颜色 */
}

/* === 4. 各界面背景色 === */
#chat-list-screen, #qzone-screen .qzone-content, #memories-view {
  background-color: #f0f2f5 !important; /* 列表页主背景色 */
}

/* === 5. 聊天输入区底部功能栏SVG图标替换 === */
/* 提示: 你需要将你的SVG代码转换为URL编码格式。
   可以使用在线工具搜索 "SVG to Data URI" 来完成转换。
   然后替换掉下面的 url('...') 部分。 */

.chat-action-icon-btn {
  background-color: rgba(255, 255, 255, 0.5); /* 图标按钮的背景色 */
  border: 1px solid rgba(0,0,0,0.05); /* 图标按钮的边框 */
}

/* 表情面板(+)按钮 */
#open-sticker-panel-btn svg { display: none; /* 隐藏原始SVG */ }
#open-sticker-panel-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 发送照片(旧)按钮 */
#send-photo-btn svg { display: none; /* 隐藏原始SVG */ }
#send-photo-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 上传图片(新)按钮 */
#upload-image-btn svg { display: none; }
#upload-image-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="color: black;"><path d="M21 3.5H3C2.44772 3.5 2 3.94772 2 4.5V19.5C2 20.0523 2.44772 20.5 3 20.5H21C21.5523 20.5 22 20.0523 22 19.5V4.5C22 3.94772 21.5523 3.5 21 3.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M16.5 13.5C17.6046 13.5 18.5 12.6046 18.5 11.5C18.5 10.3954 17.6046 9.5 16.5 9.5C15.3954 9.5 14.5 10.3954 14.5 11.5C14.5 12.6046 15.3954 13.5 16.5 13.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M22 14.5L18 10.5L10.3333 18.5M12.5 16L9 12.5L2 19.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 转账(￥)按钮 */
#transfer-btn svg { display: none; }
#transfer-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 4L12 12L17 4M12 12V20M8 10H16M8 13H16"></path></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 语音按钮 */
#voice-message-btn svg { display: none; }
#voice-message-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><path d="M12 19v4"/><path d="M8 23h8"/></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 外卖按钮 */
#send-waimai-request-btn svg { display: none; }
#send-waimai-request-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 视频通话按钮 */
#video-call-btn svg { display: none; }
#video-call-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 群视频通话按钮 */
#group-video-call-btn svg { display: none; }
#group-video-call-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 投票按钮 */
#send-poll-btn svg { display: none; }
#send-poll-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6h10"/><path d="M6 6h.01"/><path d="M8 12h10"/><path d="M6 12h.01"/><path d="M8 18h10"/><path d="M6 18h.01"/></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 分享链接按钮 */
#share-link-btn svg { display: none; }
#share-link-btn {
  background-image: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72"></path></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* 发送定位按钮 */
#send-location-btn { display: none; }
#send-location-btn {
  background-image: url('data:image/svg+xml;utf8,    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>');
  background-size: 60%;
  background-position: center;
  background-repeat: no-repeat;
}

/* === 6. 更多界面背景色 === */
/* 适用于所有设置、编辑、选择等二级页面 */
#api-settings-screen,
#font-settings-screen,
#wallpaper-screen,
#world-book-screen,
#world-book-editor-screen,
#contact-picker-screen,
#member-management-screen,
#album-screen,
#album-photos-screen,
#call-history-screen,
#chat-search-screen,
#browser-screen {
  background-color: #FAFAFA !important;
}

/* === 7. 回忆卡片美化 === */
.memory-card {
  background-color: #fffaf0 !important; /* 卡片主背景色 */
  border-left-color: #ffb74d !important; /* 左侧装饰条颜色 */
  box-shadow: 0 2px 6px rgba(0,0,0,0.07) !important;
}
.memory-card .header .author {
  color: #d98100 !important; /* 作者/标题文字颜色 */
}
.memory-card .header .date {
  color: #a1887f !important; /* 日期文字颜色 */
}
.memory-card .content {
  color: #5d4037 !important; /* 内容文字颜色 */
}
`;

// ▼▼▼ 在JS顶部，变量定义区，添加这个新常量 ▼▼▼
const DEFAULT_APP_ICONS = {
    'world-book': 'https://i.postimg.cc/HWf1JKzn/IMG-6435.jpg',
    'qq': 'https://i.postimg.cc/MTC3Tkw8/IMG-6436.jpg',
    'api-settings': 'https://i.postimg.cc/MK8rJ8t7/IMG-6438.jpg',
    'wallpaper': 'https://i.postimg.cc/T1j03pQr/IMG-6440.jpg',
    'font': 'https://i.postimg.cc/pXxk1JXk/IMG-6442.jpg',
    // ▼▼▼ 在这里添加下面这行新代码 ▼▼▼
    'check-phone': 'https://i.postimg.cc/RVwpwr0r/IMG-8348.jpg'
};
// ▲▲▲ 添加结束 ▲▲▲

        const STICKER_REGEX = /^(https:\/\/i\.postimg\.cc\/.+|https:\/\/files\.catbox\.moe\/.+|data:image)/;
        const MESSAGE_RENDER_WINDOW = 50;
        let currentRenderedCount = 0;
        let lastKnownBatteryLevel = 1;
        let alertFlags = { hasShown40: false, hasShown20: false, hasShown10: false };
        let batteryAlertTimeout;
        const dynamicFontStyle = document.createElement('style');
        dynamicFontStyle.id = 'dynamic-font-style';
        document.head.appendChild(dynamicFontStyle);

        const modalOverlay = document.getElementById('custom-modal-overlay');
        const modalTitle = document.getElementById('custom-modal-title');
        const modalBody = document.getElementById('custom-modal-body');
        const modalConfirmBtn = document.getElementById('custom-modal-confirm');
        const modalCancelBtn = document.getElementById('custom-modal-cancel');
        let modalResolve;

        function showCustomModal() { 
            modalOverlay.classList.add('visible'); 
        }

        function hideCustomModal() { 
            modalOverlay.classList.remove('visible'); 
            modalConfirmBtn.classList.remove('btn-danger'); 
            if (modalResolve) modalResolve(null); 
        }

        function showCustomConfirm(title, message, options = {}) {
            return new Promise(resolve => {
                modalResolve = resolve;
                modalTitle.textContent = title;
                modalBody.innerHTML = `<p>${message}</p>`;
                modalCancelBtn.style.display = 'block';
                modalConfirmBtn.textContent = '确定';
                if (options.confirmButtonClass) modalConfirmBtn.classList.add(options.confirmButtonClass);
                modalConfirmBtn.onclick = () => { resolve(true); hideCustomModal(); };
                modalCancelBtn.onclick = () => { resolve(false); hideCustomModal(); };
                showCustomModal();
            });
        }

        function showCustomAlert(title, message) {
            return new Promise(resolve => {
                modalResolve = resolve;
                modalTitle.textContent = title;
                modalBody.innerHTML = `<p style="text-align: left; white-space: pre-wrap;">${message}</p>`;
                modalCancelBtn.style.display = 'none';
                modalConfirmBtn.textContent = '好的';
                modalConfirmBtn.onclick = () => {
                    modalCancelBtn.style.display = 'block'; 
                    modalConfirmBtn.textContent = '确定';
                    resolve(true); 
                    hideCustomModal();
                };
                showCustomModal();
            });
        }

// ▼▼▼ 请用这个【功能增强版】替换旧的 showCustomPrompt 函数 ▼▼▼
function showCustomPrompt(title, placeholder, initialValue = '', type = 'text', extraHtml = '') {
    return new Promise(resolve => {
        modalResolve = resolve;
        modalTitle.textContent = title;
        const inputId = 'custom-prompt-input';
        
        const inputHtml = type === 'textarea' 
            ? `<textarea id="${inputId}" placeholder="${placeholder}" rows="4" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #ccc; font-size: 14px; box-sizing: border-box; resize: vertical;">${initialValue}</textarea>`
            : `<input type="${type}" id="${inputId}" placeholder="${placeholder}" value="${initialValue}">`;
        
        // 【核心修改】将额外的HTML和输入框组合在一起
        modalBody.innerHTML = extraHtml + inputHtml;
        const input = document.getElementById(inputId);

        // 【核心修改】为格式助手按钮绑定事件
        modalBody.querySelectorAll('.format-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                const templateStr = btn.dataset.template;
                if (templateStr) {
                    try {
                        const templateObj = JSON.parse(templateStr);
                        // 使用 null, 2 参数让JSON字符串格式化，带缩进，更易读
                        input.value = JSON.stringify(templateObj, null, 2);
                        input.focus();
                    } catch(e) {
                        console.error("解析格式模板失败:", e);
                    }
                }
            });
        });
        
        modalConfirmBtn.onclick = () => { resolve(input.value); hideCustomModal(); };
        modalCancelBtn.onclick = () => { resolve(null); hideCustomModal(); };
        showCustomModal();
        setTimeout(() => input.focus(), 100);
    });
}
// ▲▲▲ 替换结束 ▲▲▲

        // ===================================================================
        // 2. 数据库结构定义
        // ===================================================================

db.version(28).stores({ 
chats: '&id, isGroup, groupId, isPinned, characterPhoneData',
    apiConfig: '&id', 
    globalSettings: '&id, activeThemeId', 
    userStickers: '&id, url, name',
    worldBooks: '&id, name, categoryId', // <-- 【核心修改1】在这里添加 categoryId
    worldBookCategories: '++id, name',    // <-- 【核心修改2】新增这个表
    musicLibrary: '&id', 
    personaPresets: '&id',
    qzoneSettings: '&id',
    qzonePosts: '++id, timestamp', 
    qzoneAlbums: '++id, name, createdAt',
    qzonePhotos: '++id, albumId',
    favorites: '++id, type, timestamp, originalTimestamp',
    qzoneGroups: '++id, name',
    memories: '++id, chatId, timestamp, type, targetDate' ,
    callRecords: '++id, chatId, timestamp, customName',
    customAvatarFrames: '&id, name, url',   
    themes: '++id, name, css',
    apiPresets: '++id, name, proxyUrl, apiKey',
    bubbleStylePresets: '++id, name, css' // <-- 【核心新增】在这里添加气泡预设的“抽屉”
});

        // ===================================================================
        // 3. 所有功能函数定义
        // ===================================================================

// 【辅助函数】获取事件的坐标
function getEventCoords(e) {
    // 如果是触摸事件，就从 e.touches[0] 获取
    if (e.touches && e.touches[0]) {
        return { x: e.touches[0].pageX, y: e.touches[0].pageY };
    }
    // 否则，就是鼠标事件，直接从 e 获取
    return { x: e.pageX, y: e.pageY };
}


        function showScreen(screenId) {
            if (screenId === 'chat-list-screen') {
                window.renderChatListProxy(); 
                switchToChatListView('messages-view');
            }
            if (screenId === 'api-settings-screen') window.renderApiSettingsProxy();
            if (screenId === 'wallpaper-screen') window.renderWallpaperScreenProxy();
            if (screenId === 'world-book-screen') window.renderWorldBookScreenProxy();
            document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
            const screenToShow = document.getElementById(screenId);
            if (screenToShow) screenToShow.classList.add('active');
            if (screenId === 'chat-interface-screen') window.updateListenTogetherIconProxy(state.activeChatId);
            if (screenId === 'font-settings-screen') {
                document.getElementById('font-url-input').value = state.globalSettings.fontUrl || '';
                applyCustomFont(state.globalSettings.fontUrl || '', true);
            }
        }
        window.updateListenTogetherIconProxy = () => {};

        function switchToChatListView(viewId) {
            const chatListScreen = document.getElementById('chat-list-screen');
            const views = {
                'messages-view': document.getElementById('messages-view'),
                'qzone-screen': document.getElementById('qzone-screen'),
                'favorites-view': document.getElementById('favorites-view'),
        'memories-view': document.getElementById('memories-view') // <-- 新增这一行
    };
            const mainHeader = document.getElementById('main-chat-list-header');
            const mainBottomNav = document.getElementById('chat-list-bottom-nav'); // 获取主导航栏

            if (isFavoritesSelectionMode) {
                document.getElementById('favorites-edit-btn').click(); 
            }

            // 隐藏所有视图
            Object.values(views).forEach(v => v.classList.remove('active'));
            // 显示目标视图
            if (views[viewId]) {
                views[viewId].classList.add('active');
            }

            // 更新底部导航栏高亮
            document.querySelectorAll('#chat-list-bottom-nav .nav-item').forEach(item => {
                item.classList.toggle('active', item.dataset.view === viewId);
            });
            
            // ▼▼▼ 【核心修正】在这里统一管理所有UI元素的显隐 ▼▼▼
            if (viewId === 'messages-view') {
                mainHeader.style.display = 'flex';
                mainBottomNav.style.display = 'flex';
            } else {
                mainHeader.style.display = 'none';
                mainBottomNav.style.display = 'none';
            }
            // ▲▲▲ 修正结束 ▲▲▲

    if (viewId !== 'memories-view') {
        activeCountdownTimers.forEach(timerId => clearInterval(timerId));
        activeCountdownTimers = [];
    }

            // 根据视图ID执行特定的渲染/更新逻辑
            switch (viewId) {
                case 'qzone-screen':
                    views['qzone-screen'].style.backgroundColor = '#f0f2f5';
                    updateUnreadIndicator(0);
                    renderQzoneScreen();
                    renderQzonePosts();
                    break;
                case 'favorites-view':
                    views['favorites-view'].style.backgroundColor = '#f9f9f9';
                    renderFavoritesScreen();
                    break;
                case 'messages-view':
                    // 如果需要，可以在这里添加返回消息列表时要执行的逻辑
                    break;
            }
        }
        
        function renderQzoneScreen() {
            if (state && state.qzoneSettings) {
                const settings = state.qzoneSettings;
                document.getElementById('qzone-nickname').textContent = settings.nickname;
                document.getElementById('qzone-avatar-img').src = settings.avatar;
                document.getElementById('qzone-banner-img').src = settings.banner;
            }
        }
        window.renderQzoneScreenProxy = renderQzoneScreen;

        async function saveQzoneSettings() {
            if (db && state.qzoneSettings) {
                await db.qzoneSettings.put(state.qzoneSettings);
            }
        }

        function formatPostTimestamp(timestamp) {
            if (!timestamp) return '';
            const now = new Date();
            const date = new Date(timestamp);
            const diffSeconds = Math.floor((now - date) / 1000);
            const diffMinutes = Math.floor(diffSeconds / 60);
            const diffHours = Math.floor(diffMinutes / 60);
            if (diffMinutes < 1) return '刚刚';
            if (diffMinutes < 60) return `${diffMinutes}分钟前`;
            if (diffHours < 24) return `${diffHours}小时前`;
            const year = date.getFullYear();
            const month = String(date.getMonth() + 1).padStart(2, '0');
            const day = String(date.getDate()).padStart(2, '0');
            const hours = String(date.getHours()).padStart(2, '0');
            const minutes = String(date.getMinutes()).padStart(2, '0');
            if (now.getFullYear() === year) {
                return `${month}-${day} ${hours}:${minutes}`;
            } else {
                return `${year}-${month}-${day} ${hours}:${minutes}`;
            }
        }

// ▼▼▼ 请用这个【已添加删除按钮】的函数，完整替换掉你旧的 renderQzonePosts 函数 ▼▼▼
async function renderQzonePosts() {
    const postsListEl = document.getElementById('qzone-posts-list');
    if (!postsListEl) return;

    const [posts, favorites] = await Promise.all([
        db.qzonePosts.orderBy('timestamp').reverse().toArray(),
        db.favorites.where('type').equals('qzone_post').toArray()
    ]);

    const favoritedPostIds = new Set(favorites.map(fav => fav.content.id));
    
    postsListEl.innerHTML = '';

    if (posts.length === 0) {
        postsListEl.innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 30px 0;">这里空空如也，快来发布第一条说说吧！</p>';
        return;
    }

    const userSettings = state.qzoneSettings;

    posts.forEach(post => {
        const postContainer = document.createElement('div');
        postContainer.className = 'qzone-post-container';
        postContainer.dataset.postId = post.id;

        const postEl = document.createElement('div');
        postEl.className = 'qzone-post-item';

        let authorAvatar = '', authorNickname = '', commentAvatar = userSettings.avatar; 

        if (post.authorId === 'user') {
            authorAvatar = userSettings.avatar;
            authorNickname = userSettings.nickname;
        } else if (state.chats[post.authorId]) {
            const authorChat = state.chats[post.authorId];
            authorAvatar = authorChat.settings.aiAvatar || defaultAvatar;
            authorNickname = authorChat.name;
        } else {
            authorAvatar = defaultAvatar;
            authorNickname = '{{char}}';
        }
        
        let contentHtml = '';
        const publicTextHtml = post.publicText ? `<div class="post-content">${post.publicText.replace(/\n/g, '<br>')}</div>` : '';

        if (post.type === 'shuoshuo') {
            contentHtml = `<div class="post-content" style="margin-bottom: 10px;">${post.content.replace(/\n/g, '<br>')}</div>`;
        } 
        else if (post.type === 'image_post' && post.imageUrl) {
            contentHtml = publicTextHtml ? `${publicTextHtml}<div style="margin-top:10px;"><img src="${post.imageUrl}" class="chat-image"></div>` : `<img src="${post.imageUrl}" class="chat-image">`;
        } 
        else if (post.type === 'text_image') {
            contentHtml = publicTextHtml ? `${publicTextHtml}<div style="margin-top:10px;"><img src="https://i.postimg.cc/KYr2qRCK/1.jpg" class="chat-image" style="cursor: pointer;" data-hidden-text="${post.hiddenContent}"></div>` : `<img src="https://i.postimg.cc/KYr2qRCK/1.jpg" class="chat-image" style="cursor: pointer;" data-hidden-text="${post.hiddenContent}">`;
        }

        let likesHtml = '';
        if (post.likes && post.likes.length > 0) {
            likesHtml = `<div class="post-likes-section"><svg class="like-icon" viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg><span>${post.likes.join('、')} 觉得很赞</span></div>`;
        }
        
        let commentsHtml = '';
        if (post.comments && post.comments.length > 0) {
            commentsHtml = '<div class="post-comments-container">';
            // ★★★★★【核心修改就在这里】★★★★★
            // 遍历评论时，我们传入 comment 对象本身和它的索引 index
            post.comments.forEach((comment, index) => {
                // 在评论项的末尾，添加一个带有 data-comment-index 属性的删除按钮
                commentsHtml += `
                    <div class="comment-item">
                        <span class="commenter-name">${comment.commenterName}:</span>
                        <span class="comment-text">${comment.text}</span>
                        <span class="comment-delete-btn" data-comment-index="${index}">×</span>
                    </div>`;
            });
            // ★★★★★【修改结束】★★★★★
            commentsHtml += '</div>';
        }

        const userNickname = state.qzoneSettings.nickname;
        const isLikedByUser = post.likes && post.likes.includes(userNickname);
        const isFavoritedByUser = favoritedPostIds.has(post.id);

        postEl.innerHTML = `
            <div class="post-header"><img src="${authorAvatar}" class="post-avatar"><div class="post-info"><span class="post-nickname">${authorNickname}</span><span class="post-timestamp">${formatPostTimestamp(post.timestamp)}</span></div>
                <div class="post-actions-btn">…</div>
            </div>
            <div class="post-main-content">${contentHtml}</div>
            <div class="post-feedback-icons">
                <span class="action-icon like ${isLikedByUser ? 'active' : ''}"><svg viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg></span>
                <span class="action-icon favorite ${isFavoritedByUser ? 'active' : ''}"><svg viewBox="0 0 24 24"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path></svg></span>
            </div>
            ${likesHtml}
            ${commentsHtml}
            <div class="post-footer"><div class="comment-section"><img src="${commentAvatar}" class="comment-avatar"><input type="text" class="comment-input" placeholder="友善的评论是交流的起点"><div class="at-mention-popup"></div></div><button class="comment-send-btn">发送</button></div>
        `;
        
        const deleteAction = document.createElement('div');
        deleteAction.className = 'qzone-post-delete-action';
        deleteAction.innerHTML = '<span>删除</span>';
        postContainer.appendChild(postEl);
        postContainer.appendChild(deleteAction);
        const commentSection = postContainer.querySelector('.comment-section');
        if (commentSection) {
            commentSection.addEventListener('touchstart', (e) => e.stopPropagation());
            commentSection.addEventListener('mousedown', (e) => e.stopPropagation());
        }
        postsListEl.appendChild(postContainer);
        const commentInput = postContainer.querySelector('.comment-input');
        const popup = postContainer.querySelector('.at-mention-popup');
        commentInput.addEventListener('input', () => {
            const value = commentInput.value;
            const atMatch = value.match(/@([\p{L}\w]*)$/u);
            if (atMatch) {
                const namesToMention = new Set();
                const authorNickname = postContainer.querySelector('.post-nickname')?.textContent;
                if (authorNickname) namesToMention.add(authorNickname);
                postContainer.querySelectorAll('.commenter-name').forEach(nameEl => {
                    namesToMention.add(nameEl.textContent.replace(':', ''));
                });
                namesToMention.delete(state.qzoneSettings.nickname);
                popup.innerHTML = '';
                if (namesToMention.size > 0) {
                    const searchTerm = atMatch[1];
                    namesToMention.forEach(name => {
                        if (name.toLowerCase().includes(searchTerm.toLowerCase())) {
                            const item = document.createElement('div');
                            item.className = 'at-mention-item';
                            item.textContent = name;
                            item.addEventListener('mousedown', (e) => {
                                e.preventDefault();
                                const newText = value.substring(0, atMatch.index) + `@${name} `;
                                commentInput.value = newText;
                                popup.style.display = 'none';
                                commentInput.focus();
                            });
                            popup.appendChild(item);
                        }
                    });
                    popup.style.display = popup.children.length > 0 ? 'block' : 'none';
                } else {
                    popup.style.display = 'none';
                }
            } else {
                popup.style.display = 'none';
            }
        });
        commentInput.addEventListener('blur', () => { setTimeout(() => { popup.style.display = 'none'; }, 200); });
    });
}
// ▲▲▲ 替换结束 ▲▲▲
             
// ▼▼▼ 请用下面这个【更新后的】函数，完整替换掉你代码中旧的 displayFilteredFavorites 函数 ▼▼▼

function displayFilteredFavorites(items) {
    const listEl = document.getElementById('favorites-list');
    listEl.innerHTML = '';

    if (items.length === 0) {
        const searchTerm = document.getElementById('favorites-search-input').value;
        const message = searchTerm ? '未找到相关收藏' : '你的收藏夹是空的，<br>快去动态或聊天中收藏喜欢的内容吧！';
        listEl.innerHTML = `<p style="text-align:center; color: var(--text-secondary); padding: 50px 0;">${message}</p>`;
        return;
    }

    for (const item of items) {
        const card = document.createElement('div');
        card.className = 'favorite-item-card';
        card.dataset.favid = item.id;

        let headerHtml = '', contentHtml = '', sourceText = '', footerHtml = '';

        if (item.type === 'qzone_post') {
            const post = item.content;
            sourceText = '来自动态';
            let authorAvatar = defaultAvatar, authorNickname = '未知用户';

            if (post.authorId === 'user') {
                authorAvatar = state.qzoneSettings.avatar;
                authorNickname = state.qzoneSettings.nickname;
            } else if (state.chats[post.authorId]) {
                authorAvatar = state.chats[post.authorId].settings.aiAvatar;
                authorNickname = state.chats[post.authorId].name;
            }

            headerHtml = `<img src="${authorAvatar}" class="avatar"><div class="info"><div class="name">${authorNickname}</div></div>`;
            
            const publicTextHtml = post.publicText ? `<div class="post-content">${post.publicText.replace(/\n/g, '<br>')}</div>` : '';
            if (post.type === 'shuoshuo') {
                contentHtml = `<div class="post-content">${post.content.replace(/\n/g, '<br>')}</div>`;
            } else if (post.type === 'image_post' && post.imageUrl) {
                contentHtml = publicTextHtml ? `${publicTextHtml}<div style="margin-top:10px;"><img src="${post.imageUrl}" class="chat-image"></div>` : `<img src="${post.imageUrl}" class="chat-image">`;
            } else if (post.type === 'text_image') {
                contentHtml = publicTextHtml ? `${publicTextHtml}<div style="margin-top:10px;"><img src="https://i.postimg.cc/KYr2qRCK/1.jpg" class="chat-image" style="cursor: pointer;" data-hidden-text="${post.hiddenContent}"></div>` : `<img src="https://i.postimg.cc/KYr2qRCK/1.jpg" class="chat-image" style="cursor: pointer;" data-hidden-text="${post.hiddenContent}">`;
            }

            // ▼▼▼ 新增/修改的代码开始 ▼▼▼
            
            // 1. 构造点赞区域的HTML
            let likesHtml = '';
            // 检查 post 对象中是否存在 likes 数组并且不为空
            if (post.likes && post.likes.length > 0) {
                // 如果存在，就创建点赞区域的 div
                likesHtml = `
                    <div class="post-likes-section">
                        <svg class="like-icon" viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
                        <span>${post.likes.join('、')} 觉得很赞</span>
                    </div>`;
            }

            // 2. 构造评论区域的HTML
            let commentsHtml = '';
            // 检查 post 对象中是否存在 comments 数组并且不为空
            if (post.comments && post.comments.length > 0) {
                // 如果存在，就创建评论容器，并遍历每一条评论
                commentsHtml = '<div class="post-comments-container">';
                post.comments.forEach(comment => {
                    commentsHtml += `
                        <div class="comment-item">
                            <span class="commenter-name">${comment.commenterName}:</span>
                            <span class="comment-text">${comment.text}</span>
                        </div>`;
                });
                commentsHtml += '</div>';
            }

            // 3. 将点赞和评论的HTML组合到 footerHtml 中
            footerHtml = `${likesHtml}${commentsHtml}`;
            
            // ▲▲▲ 新增/修改的代码结束 ▲▲▲

} else if (item.type === 'chat_message') {
    const msg = item.content;
    const chat = state.chats[item.chatId];
    if (!chat) continue; 

    sourceText = `来自与 ${chat.name} 的聊天`;
    const isUser = msg.role === 'user';
    let senderName, senderAvatar;

    if (isUser) {
        // 用户消息的逻辑保持不变
        senderName = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
        senderAvatar = chat.settings.myAvatar || (chat.isGroup ? defaultMyGroupAvatar : defaultAvatar);
    } else { // AI/成员消息
         if (chat.isGroup) {
            // ★★★★★ 这就是唯一的、核心的修改！ ★★★★★
            // 我们现在使用 originalName 去匹配，而不是旧的 name
            const member = chat.members.find(m => m.originalName === msg.senderName);
            // ★★★★★ 修改结束 ★★★★★
            
            senderName = msg.senderName;
            // 因为现在能正确找到 member 对象了，所以也能正确获取到他的头像
            senderAvatar = member ? member.avatar : defaultGroupMemberAvatar;
        } else {
            // 单聊的逻辑保持不变
            senderName = chat.name;
            senderAvatar = chat.settings.aiAvatar || defaultAvatar;
        }
    }

    // 后续拼接 headerHtml 和 contentHtml 的逻辑都保持不变
    headerHtml = `<img src="${senderAvatar}" class="avatar"><div class="info"><div class="name">${senderName}</div></div>`;
    
    if (typeof msg.content === 'string' && STICKER_REGEX.test(msg.content)) {
        contentHtml = `<img src="${msg.content}" class="sticker-image" style="max-width: 80px; max-height: 80px;">`;
    } else if (Array.isArray(msg.content) && msg.content[0]?.type === 'image_url') {
        contentHtml = `<img src="${msg.content[0].image_url.url}" class="chat-image">`;
    } else {
        contentHtml = String(msg.content || '').replace(/\n/g, '<br>');
    }
}
        
        // ▼▼▼ 修改最终的HTML拼接，加入 footerHtml ▼▼▼
        card.innerHTML = `
            <div class="fav-card-header">${headerHtml}<div class="source">${sourceText}</div></div>
            <div class="fav-card-content">${contentHtml}</div>
            ${footerHtml}`; // <-- 把我们新创建的 footerHtml 放在这里
            
        listEl.appendChild(card);
    }
}

// ▲▲▲ 替换区域结束 ▲▲▲

        /**
         * 【重构后的函数】: 负责准备数据并触发渲染
         */
        async function renderFavoritesScreen() {
            // 1. 从数据库获取最新数据并缓存
            allFavoriteItems = await db.favorites.orderBy('timestamp').reverse().toArray();
            
            // 2. 清空搜索框并隐藏清除按钮
            const searchInput = document.getElementById('favorites-search-input');
            const clearBtn = document.getElementById('favorites-search-clear-btn');
            searchInput.value = '';
            clearBtn.style.display = 'none';

            // 3. 显示所有收藏项
            displayFilteredFavorites(allFavoriteItems);
        }

        // ▲▲▲ 粘贴结束 ▲▲▲

        function resetCreatePostModal() {
            document.getElementById('post-public-text').value = '';
            document.getElementById('post-image-preview').src = '';
            document.getElementById('post-image-description').value = '';
            document.getElementById('post-image-preview-container').classList.remove('visible');
            document.getElementById('post-image-desc-group').style.display = 'none';
            document.getElementById('post-local-image-input').value = '';
            document.getElementById('post-hidden-text').value = '';
            document.getElementById('switch-to-image-mode').click();
        }

// ▼▼▼ 用这个【已包含 memories】的版本，完整替换旧的 exportBackup 函数 ▼▼▼
async function exportBackup() {
    try {
        const backupData = {
            version: 1, 
            timestamp: Date.now()
        };

        const [
            chats, worldBooks, userStickers, apiConfig, globalSettings,
            personaPresets, musicLibrary, qzoneSettings, qzonePosts,
            qzoneAlbums, qzonePhotos, favorites, qzoneGroups,
            memories,
                worldBookCategories,
    callRecords,
    customAvatarFrames, 
            themes,
apiPresets,
bubbleStylePresets
        ] = await Promise.all([
            db.chats.toArray(),
            db.worldBooks.toArray(),
            db.userStickers.toArray(),
            db.apiConfig.get('main'),
            db.globalSettings.get('main'),
            db.personaPresets.toArray(),
            db.musicLibrary.get('main'),
            db.qzoneSettings.get('main'),
            db.qzonePosts.toArray(),
            db.qzoneAlbums.toArray(),
            db.qzonePhotos.toArray(),
            db.favorites.toArray(),
            db.qzoneGroups.toArray(),
                        db.memories.toArray(),
            db.worldBookCategories.toArray(),
    db.callRecords.toArray(),       
    db.customAvatarFrames.toArray(),

            db.themes.toArray(),

db.apiPresets.toArray(),

db.bubbleStylePresets.toArray()
]);

        Object.assign(backupData, {
            chats, worldBooks, userStickers, apiConfig, globalSettings,
            personaPresets, musicLibrary, qzoneSettings, qzonePosts,
            qzoneAlbums, qzonePhotos, favorites, qzoneGroups,
            memories,
                worldBookCategories,
    callRecords,          
    customAvatarFrames, 
            themes,
apiPresets,
bubbleStylePresets
});
        
        const blob = new Blob(
            [JSON.stringify(backupData, null, 2)], 
            { type: 'application/json' }
        );
        const url = URL.createObjectURL(blob);
        const link = Object.assign(document.createElement('a'), {
            href: url,
            download: `EPhone-Full-Backup-${new Date().toISOString().split('T')[0]}.json`
        });
        link.click();
        URL.revokeObjectURL(url);
        
        await showCustomAlert('导出成功', '已成功导出所有数据！');

    } catch (error) {
        console.error("导出数据时出错:", error);
        await showCustomAlert('导出失败', `发生了一个错误: ${error.message}`);
    }
}

// ▼▼▼ 用这个【已包含 memories】的版本，完整替换旧的 importBackup 函数 ▼▼▼
async function importBackup(file) {
    if (!file) return;

    const confirmed = await showCustomConfirm(
        '严重警告！',
        '导入备份将完全覆盖您当前的所有数据，包括聊天、动态、设置等。此操作不可撤销！您确定要继续吗？',
        { confirmButtonClass: 'btn-danger' }
    );

    if (!confirmed) return;

    try {
        const text = await file.text();
        const data = JSON.parse(text);

        await db.transaction('rw', db.tables, async () => {
            for (const table of db.tables) {
                await table.clear();
            }

            if (Array.isArray(data.chats)) await db.chats.bulkPut(data.chats);
            if (Array.isArray(data.worldBooks)) await db.worldBooks.bulkPut(data.worldBooks);
            if (Array.isArray(data.worldBookCategories)) await db.worldBookCategories.bulkPut(data.worldBookCategories);
            if (Array.isArray(data.userStickers)) await db.userStickers.bulkPut(data.userStickers);
            if (Array.isArray(data.personaPresets)) await db.personaPresets.bulkPut(data.personaPresets);
            if (Array.isArray(data.qzonePosts)) await db.qzonePosts.bulkPut(data.qzonePosts);
            if (Array.isArray(data.qzoneAlbums)) await db.qzoneAlbums.bulkPut(data.qzoneAlbums);
            if (Array.isArray(data.qzonePhotos)) await db.qzonePhotos.bulkPut(data.qzonePhotos);
            if (Array.isArray(data.favorites)) await db.favorites.bulkPut(data.favorites);
            if (Array.isArray(data.qzoneGroups)) await db.qzoneGroups.bulkPut(data.qzoneGroups);
if (Array.isArray(data.memories)) await db.memories.bulkPut(data.memories);
if (Array.isArray(data.apiPresets)) await db.apiPresets.bulkPut(data.apiPresets);
if (Array.isArray(data.bubbleStylePresets)) await db.bubbleStylePresets.bulkPut(data.bubbleStylePresets);
if (Array.isArray(data.callRecords)) await db.callRecords.bulkPut(data.callRecords);             // <--- 新增
if (Array.isArray(data.customAvatarFrames)) await db.customAvatarFrames.bulkPut(data.customAvatarFrames); // <--- 新增
            if (Array.isArray(data.themes)) await db.themes.bulkPut(data.themes);

            if (data.apiConfig) await db.apiConfig.put(data.apiConfig);
            if (data.globalSettings) await db.globalSettings.put(data.globalSettings);
            if (data.musicLibrary) await db.musicLibrary.put(data.musicLibrary);
            if (data.qzoneSettings) await db.qzoneSettings.put(data.qzoneSettings);
        });

        await showCustomAlert('导入成功', '所有数据已成功恢复！应用即将刷新以应用所有更改。');
        
        setTimeout(() => {
            window.location.reload();
        }, 1500);

    } catch (error) {
        console.error("导入数据时出错:", error);
        await showCustomAlert('导入失败', `文件格式不正确或数据已损坏: ${error.message}`);
    }
}

        function applyCustomFont(fontUrl, isPreviewOnly = false) {
            if (!fontUrl) {
                dynamicFontStyle.innerHTML = '';
                document.getElementById('font-preview').style.fontFamily = '';
                return;
            }
            const fontName = 'custom-user-font';
            const newStyle = `
                @font-face {
                  font-family: '${fontName}';
                  src: url('${fontUrl}');
                  font-display: swap;
                }`;
            if (isPreviewOnly) {
                const previewStyle = document.getElementById('preview-font-style') || document.createElement('style');
                previewStyle.id = 'preview-font-style';
                previewStyle.innerHTML = newStyle;
                if (!document.getElementById('preview-font-style')) document.head.appendChild(previewStyle);
                document.getElementById('font-preview').style.fontFamily = `'${fontName}', 'bulangni', sans-serif`;
            } else {
                dynamicFontStyle.innerHTML = `
                    ${newStyle}
                    body {
                      font-family: '${fontName}', 'bulangni', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                    }`;
            }
        }

        async function resetToDefaultFont() {
            dynamicFontStyle.innerHTML = ''; 
            state.globalSettings.fontUrl = '';
            await db.globalSettings.put(state.globalSettings);
            document.getElementById('font-url-input').value = '';
            document.getElementById('font-preview').style.fontFamily = '';
            alert('已恢复默认字体。');
        }

async function loadAllDataFromDB() {
    // ▼▼▼ 【核心修改在这里】 ▼▼▼
    const [
        chatsArr,
        apiConfig,
        globalSettings,
        userStickers,
        worldBooks,
        musicLib,
        personaPresets,
        qzoneSettings,
        initialFavorites,
    apiPresets,
bubbleStylePresets
] = await Promise.all([
        db.chats.toArray(),
        db.apiConfig.get('main'),
        db.globalSettings.get('main'),
        db.userStickers.toArray(),
        db.worldBooks.toArray(),
        db.musicLibrary.get('main'),
        db.personaPresets.toArray(),
        db.qzoneSettings.get('main'),
        db.favorites.orderBy('timestamp').reverse().toArray(),
    db.apiPresets.toArray(),    db.bubbleStylePresets.toArray()
]);
    // ▲▲▲ 【修改结束】 ▲▲▲

    state.chats = chatsArr.reduce((acc, chat) => {

    if (typeof chat.unreadCount === 'undefined') {
        chat.unreadCount = 0; // 如果这个聊天对象没有 unreadCount 属性，就给它初始化为 0
    }

        // ★★★【核心重构：数据迁移脚本】★★★
        // 检查是否是群聊，并且其成员对象使用的是旧的 `name` 结构
        if (chat.isGroup && chat.members && chat.members.length > 0 && chat.members[0].name) {
            console.log(`检测到旧版群聊数据 for "${chat.name}"，正在执行迁移...`);
            chat.members.forEach(member => {
                // 如果这个成员对象没有 originalName，说明是旧数据
                if (typeof member.originalName === 'undefined') {
                    member.originalName = member.name; // 将旧的 name 作为 originalName
                    member.groupNickname = member.name; // 同时创建一个初始的 groupNickname
                    delete member.name; // 删除旧的、有歧义的 name 字段
                    needsUpdate = true; // 标记需要存回数据库
                }
            });
             console.log(`迁移完成 for "${chat.name}"`);
        }

        // --- ▼▼▼ 核心修复就在这里 ▼▼▼ ---
        // 检查1：如果是一个单聊，并且没有 status 属性
        if (!chat.isGroup && !chat.status) {
            // 就为它补上一个默认的 status 对象
            chat.status = {
                text: '在线',
                lastUpdate: Date.now(),
                isBusy: false
            };
            console.log(`为旧角色 "${chat.name}" 补全了status属性。`);
        }
        // --- ▲▲▲ 修复结束 ▲▲▲

        // --- ▼▼▼ 核心修复就在这里 ▼▼▼ ---
        // 检查2：兼容最新的“关系”功能
        if (!chat.isGroup && !chat.relationship) {
            // 如果是单聊，且没有 relationship 对象，就补上一个默认的
            chat.relationship = {
                status: 'friend',
                blockedTimestamp: null,
                applicationReason: ''
            };
            console.log(`为旧角色 "${chat.name}" 补全了 relationship 属性。`);
        }
        // --- ▲▲▲ 修复结束 ▲▲▲

    // ▼▼▼ 在这里添加 ▼▼▼
    if (!chat.isGroup && (!chat.settings || !chat.settings.aiAvatarLibrary)) {
        if (!chat.settings) chat.settings = {}; // 以防万一连settings都没有
        chat.settings.aiAvatarLibrary = [];
        console.log(`为旧角色 "${chat.name}" 补全了aiAvatarLibrary属性。`);
    }
    // ▲▲▲ 添加结束 ▲▲▲

        if (!chat.musicData) chat.musicData = { totalTime: 0 };
        if (chat.settings && chat.settings.linkedWorldBookId && !chat.settings.linkedWorldBookIds) {
            chat.settings.linkedWorldBookIds = [chat.settings.linkedWorldBookId];
            delete chat.settings.linkedWorldBookId;
        }

    // 兼容旧数据，为没有 isPinned 属性的聊天添加默认值
    if (typeof chat.isPinned === 'undefined') {
        chat.isPinned = false;
    }

// ▼▼▼ 【V2最终修复版】统一修复并初始化所有角色的手机数据 ▼▼▼
if (!chat.isGroup) {
    // 第一步：确保最外层的 characterPhoneData 对象存在
    if (!chat.characterPhoneData) {
        chat.characterPhoneData = {}; // 如果不存在，就创建一个空的
    }

    // 第二步：逐一检查并补全所有APP的数据结构
    // 这样无论角色多老，都能确保所有字段都存在
    if (!chat.characterPhoneData.lastGenerated) chat.characterPhoneData.lastGenerated = null;
    if (!chat.characterPhoneData.chats) chat.characterPhoneData.chats = {};
    if (!chat.characterPhoneData.shoppingCart) chat.characterPhoneData.shoppingCart = [];
    if (!chat.characterPhoneData.memos) chat.characterPhoneData.memos = [];
    if (!chat.characterPhoneData.browserHistory) chat.characterPhoneData.browserHistory = [];
    if (!chat.characterPhoneData.photoAlbum) chat.characterPhoneData.photoAlbum = [];
    if (!chat.characterPhoneData.bank) chat.characterPhoneData.bank = { balance: 0, transactions: [] };
    if (!chat.characterPhoneData.trajectory) chat.characterPhoneData.trajectory = [];
    if (!chat.characterPhoneData.appUsage) chat.characterPhoneData.appUsage = [];
    if (!chat.characterPhoneData.diary) chat.characterPhoneData.diary = []; // 核心修复！
}
// ▲▲▲ 修正结束 ▲▲▲

        acc[chat.id] = chat;
        return acc;
    }, {});
    state.apiConfig = apiConfig || { id: 'main', proxyUrl: '', apiKey: '', model: '' };

state.globalSettings = globalSettings || { 
    id: 'main', 
    wallpaper: 'linear-gradient(135deg, #89f7fe, #66a6ff)', 
    lockscreenWallpaper: 'linear-gradient(135deg, #764ba2, #667eea)', // <-- 新增：默认锁屏壁纸
    password: '', // <-- 新增：默认无密码
    fontUrl: '', 
    enableBackgroundActivity: false, 
    backgroundActivityInterval: 60,
    blockCooldownHours: 1,
    appIcons: { ...DEFAULT_APP_ICONS },
    ringtoneUrl: 'https://files.catbox.moe/3w7gla.mp3' // <-- 【核心新增】在这里添加默认铃声URL
};

    // ▼▼▼ 在这里添加下面这行代码 ▼▼▼
    // 【核心新增】加载歌词栏设置，如果不存在则使用默认值
    lyricsBarSettings = state.globalSettings.lyricsBarSettings || lyricsBarSettings;
    // ▲▲▲ 添加结束 ▲▲▲

// 【核心修改】合并已保存的图标和默认图标，防止更新后旧数据丢失新图标
state.globalSettings.appIcons = { ...DEFAULT_APP_ICONS, ...(state.globalSettings.appIcons || {}) };

    state.userStickers = userStickers || [];
    state.worldBooks = worldBooks || [];
    musicState.playlist = musicLib?.playlist || [];
    state.personaPresets = personaPresets || [];
state.apiPresets = apiPresets || [];
state.bubbleStylePresets = bubbleStylePresets || [];
    state.qzoneSettings = qzoneSettings || { id: 'main', nickname: '{{user}}', avatar: 'https://files.catbox.moe/q6z5fc.jpeg', banner: 'https://files.catbox.moe/r5heyt.gif' };

    // ▼▼▼ 【确保这一行在 Promise.all 之后，并使用解构赋值得到的 initialFavorites】 ▼▼▼
    allFavoriteItems = initialFavorites || [];
    // ▲▲▲ 【修改结束】 ▲▲▲
}

        async function saveGlobalPlaylist() { await db.musicLibrary.put({ id: 'main', playlist: musicState.playlist }); }

        function formatTimestamp(timestamp) { if (!timestamp) return ''; const date = new Date(timestamp); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); return `${hours}:${minutes}`; }

        function showNotification(chatId, messageContent) { clearTimeout(notificationTimeout); const chat = state.chats[chatId]; if (!chat) return; const bar = document.getElementById('notification-bar'); document.getElementById('notification-avatar').src = chat.settings.aiAvatar || chat.settings.groupAvatar || defaultAvatar; document.getElementById('notification-content').querySelector('.name').textContent = chat.name; document.getElementById('notification-content').querySelector('.message').textContent = messageContent; const newBar = bar.cloneNode(true); bar.parentNode.replaceChild(newBar, bar); newBar.addEventListener('click', () => { openChat(chatId); newBar.classList.remove('visible'); }); newBar.classList.add('visible'); notificationTimeout = setTimeout(() => { newBar.classList.remove('visible'); }, 4000); }

        function updateClock() { const now = new Date(); const timeString = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); const dateString = now.toLocaleDateString('zh-CN', { weekday: 'long', month: 'long', day: 'numeric' }); document.getElementById('main-time').textContent = timeString; document.getElementById('status-bar-time').textContent = timeString; document.getElementById('main-date').textContent = dateString; 
    // 更新锁屏时钟 (新增)
    updateLockClock();
}

/**
 * 【终极健壮版】解析AI返回的、可能格式不规范的响应内容
 * @param {string} content - AI返回的原始字符串
 * @returns {Array} - 一个标准化的消息对象数组
 */
function parseAiResponse(content) {
    const trimmedContent = content.trim();

    // 方案1：【最优先】尝试作为标准的、单一的JSON数组解析
    // 这是最理想、最高效的情况
    if (trimmedContent.startsWith('[') && trimmedContent.endsWith(']')) {
        try {
            const parsed = JSON.parse(trimmedContent);
            if (Array.isArray(parsed)) {
                console.log("解析成功：标准JSON数组格式。");
                return parsed;
            }
        } catch (e) {
            // 如果解析失败，说明它虽然看起来像个数组，但内部格式有问题。
            // 此时我们不报错，而是继续尝试下面的“强力解析”方案。
            console.warn("标准JSON数组解析失败，将尝试强力解析...");
        }
    }

    // 方案2：【强力解析】使用正则表达式，从混乱的字符串中提取出所有独立的JSON对象
    // 这能完美解决您遇到的 "(Timestamp: ...)[{...}](Timestamp: ...)[{...}]" 这种格式
    const jsonMatches = trimmedContent.match(/{[^{}]*}/g);

    if (jsonMatches) {
        const results = [];
        for (const match of jsonMatches) {
            try {
                // 尝试解析每一个被我们“揪”出来的JSON字符串
                const parsedObject = JSON.parse(match);
                results.push(parsedObject);
            } catch (e) {
                // 如果某个片段不是有效的JSON，就忽略它，继续处理下一个
                console.warn("跳过一个无效的JSON片段:", match);
            }
        }

        // 如果我们成功提取出了至少一个有效的JSON对象，就返回这个结果
        if (results.length > 0) {
            console.log("解析成功：通过强力提取模式。");
            return results;
        }
    }
    
    // 方案3：【最终备用】如果以上所有方法都失败了，说明AI返回的可能就是纯文本
    // 我们将原始的、未处理的内容，包装成一个标准的文本消息对象返回，确保程序不会崩溃
    console.error("所有解析方案均失败！将返回原始文本。");
    return [{ type: 'text', content: content }];
}

        function renderApiSettings() { document.getElementById('proxy-url').value = state.apiConfig.proxyUrl || ''; document.getElementById('api-key').value = state.apiConfig.apiKey || ''; 
    // ▼▼▼ 新增这行 ▼▼▼
    document.getElementById('background-activity-switch').checked = state.globalSettings.enableBackgroundActivity || false;
    document.getElementById('background-interval-input').value = state.globalSettings.backgroundActivityInterval || 60;
    document.getElementById('block-cooldown-input').value = state.globalSettings.blockCooldownHours || 1;

   renderApiPresetSelector(); // 👈 在这里添加这一行
}
        window.renderApiSettingsProxy = renderApiSettings;

// ▼▼▼ 请用这个【全新修正版】替换旧的 renderChatList 函数 ▼▼▼
async function renderChatList() {
    const chatListEl = document.getElementById('chat-list');
    chatListEl.innerHTML = '';

    // 1. 获取所有聊天和分组数据
    const allChats = Object.values(state.chats);
    const allGroups = await db.qzoneGroups.toArray();

    if (allChats.length === 0) {
        chatListEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">点击右上角 "+" 或群组图标添加聊天</p>';
        return;
    }

    // 2. 将聊天明确地分为“置顶”和“未置顶”两组
    const pinnedChats = allChats.filter(chat => chat.isPinned);
    const unpinnedChats = allChats.filter(chat => !chat.isPinned);

    // 3. 对置顶的聊天，仅按最新消息时间排序
    pinnedChats.sort((a, b) => (b.history.slice(-1)[0]?.timestamp || 0) - (a.history.slice(-1)[0]?.timestamp || 0));

    // 4. 【优先渲染】所有置顶的聊天
    pinnedChats.forEach(chat => {
        const item = createChatListItem(chat);
        chatListEl.appendChild(item);
    });

    // 5. 【接下来处理未置顶的聊天】应用您之前的分组逻辑
    // 为每个分组找到其内部最新的消息时间戳 (只在未置顶聊天中查找)
    allGroups.forEach(group => {
        const latestChatInGroup = unpinnedChats
            .filter(chat => chat.groupId === group.id) // 找到属于这个组的聊天
            .sort((a, b) => (b.history.slice(-1)[0]?.timestamp || 0) - (a.history.slice(-1)[0]?.timestamp || 0))[0]; // 排序后取第一个
        
        group.latestTimestamp = latestChatInGroup ? (latestChatInGroup.history.slice(-1)[0]?.timestamp || 0) : 0;
    });

    // 根据分组的最新时间戳，对分组本身进行排序
    allGroups.sort((a, b) => b.latestTimestamp - a.latestTimestamp);

    // 6. 遍历排序后的分组，渲染其中的【未置顶】好友
    allGroups.forEach(group => {
        const groupChats = unpinnedChats
            .filter(chat => !chat.isGroup && chat.groupId === group.id)
            .sort((a, b) => (b.history.slice(-1)[0]?.timestamp || 0) - (a.history.slice(-1)[0]?.timestamp || 0));
        
        if (groupChats.length === 0) return; // 如果这个分组里没有未置顶的好友，就跳过

        const groupContainer = document.createElement('div');
        groupContainer.className = 'chat-group-container';
        groupContainer.innerHTML = `
            <div class="chat-group-header">
                <span class="arrow">▼</span>
                <span class="group-name">${group.name}</span>
            </div>
            <div class="chat-group-content"></div>
        `;
        const contentEl = groupContainer.querySelector('.chat-group-content');
        groupChats.forEach(chat => {
            const item = createChatListItem(chat);
            contentEl.appendChild(item);
        });
        chatListEl.appendChild(groupContainer);
    });

    // 7. 最后，渲染所有【未置顶】的群聊和【未分组的】好友
    const remainingChats = unpinnedChats
        .filter(chat => chat.isGroup || (!chat.isGroup && !chat.groupId))
        .sort((a, b) => (b.history.slice(-1)[0]?.timestamp || 0) - (a.history.slice(-1)[0]?.timestamp || 0));
    
    remainingChats.forEach(chat => {
        const item = createChatListItem(chat);
        chatListEl.appendChild(item);
    });

    // 为所有分组标题添加折叠事件
    document.querySelectorAll('.chat-group-header').forEach(header => {
        header.addEventListener('click', () => {
            header.classList.toggle('collapsed');
            header.nextElementSibling.classList.toggle('collapsed');
        });
    });
}
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 用这个【最终修正版】替换你项目中那个导致错误的 createChatListItem 函数 ▼▼▼
function createChatListItem(chat) {
    const lastMsgObj = chat.history.filter(msg => !msg.isHidden).slice(-1)[0] || {};
    let lastMsgDisplay;

    // --- 消息预览的逻辑保持不变 ---
    if (!chat.isGroup && chat.relationship?.status === 'pending_user_approval') {
        lastMsgDisplay = `<span style="color: #ff8c00;">[好友申请] ${chat.relationship.applicationReason || '请求添加你为好友'}</span>`;
    } else if (!chat.isGroup && chat.relationship?.status === 'blocked_by_ai') {
        lastMsgDisplay = `<span style="color: #dc3545;">[你已被对方拉黑]</span>`;
    } else if (chat.isGroup) {
        if (lastMsgObj.type === 'pat_message') { lastMsgDisplay = `[系统消息] ${lastMsgObj.content}`; }
        else if (lastMsgObj.type === 'transfer') { lastMsgDisplay = '[转账]'; }
        else if (lastMsgObj.type === 'ai_image' || lastMsgObj.type === 'user_photo') { lastMsgDisplay = '[照片]'; }
        else if (lastMsgObj.type === 'voice_message') { lastMsgDisplay = '[语音]'; }
        else if (typeof lastMsgObj.content === 'string' && STICKER_REGEX.test(lastMsgObj.content)) { lastMsgDisplay = lastMsgObj.meaning ? `[表情: ${lastMsgObj.meaning}]` : '[表情]'; }
        else if (Array.isArray(lastMsgObj.content)) { lastMsgDisplay = `[图片]`; }
        else { lastMsgDisplay = String(lastMsgObj.content || '...').substring(0, 20); }
        if (lastMsgObj.senderName && lastMsgObj.type !== 'pat_message') {
            lastMsgDisplay = `${lastMsgObj.senderName}: ${lastMsgDisplay}`;
        }
    } else {
        const statusText = chat.status?.text || '在线';
        lastMsgDisplay = `[${statusText}]`;
    }

    const lastMsgTimestamp = lastMsgObj?.timestamp;
    const timeDisplay = formatChatListTimestamp(lastMsgTimestamp);
    
    const container = document.createElement('div');
    container.className = 'chat-list-item-swipe-container';
    container.dataset.chatId = chat.id;

    const content = document.createElement('div');
    content.className = `chat-list-item-content ${chat.isPinned ? 'pinned' : ''}`;
    
    const avatar = chat.isGroup ? chat.settings.groupAvatar : chat.settings.aiAvatar;
    
    // --- 【核心修正】在这里，我们创建了一个新的右侧容器，并把时间和未读红点都放了进去 ---
    content.innerHTML = `
        <div class="chat-list-item" data-chat-id="${chat.id}">
            <img src="${avatar || defaultAvatar}" class="avatar">
            <div class="info">
                <div class="name-line">
                    <span class="name">${chat.name}</span>
                    ${chat.isGroup ? '<span class="group-tag">群聊</span>' : ''}
                </div>
                <div class="last-msg" style="color: ${chat.isGroup ? 'var(--text-secondary)' : '#b5b5b5'}; font-style: italic;">${lastMsgDisplay}</div>
            </div>
            <div class="chat-list-right-column">
                <div class="chat-list-time">${timeDisplay}</div>
                <div class="unread-count-wrapper">
                    <span class="unread-count" style="display: none;">0</span>
                </div>
            </div>
        </div>
    `;
    
    // 后续的滑动按钮逻辑保持不变
    const actions = document.createElement('div');
    actions.className = 'swipe-actions';
    const pinButtonText = chat.isPinned ? '取消置顶' : '置顶';
    const pinButtonClass = chat.isPinned ? 'unpin' : 'pin';
    actions.innerHTML = `
        <button class="swipe-action-btn ${pinButtonClass}">${pinButtonText}</button>
        <button class="swipe-action-btn delete">删除</button>
    `;
    
    container.appendChild(content);
    container.appendChild(actions);
    
    // --- 【重要】现在这段代码可以正常工作了，因为 .unread-count 元素又回来了！ ---
    const unreadCount = chat.unreadCount || 0;
    const unreadEl = content.querySelector('.unread-count');
    if (unreadCount > 0) {
        unreadEl.textContent = unreadCount > 99 ? '99+' : unreadCount;
        unreadEl.style.display = 'inline-flex';
    } else {
        unreadEl.style.display = 'none';
    }
    
    // 事件监听逻辑保持不变
    const infoEl = content.querySelector('.info');
    if (infoEl) {
        infoEl.addEventListener('click', () => openChat(chat.id));
    }
const avatarEl = content.querySelector('.avatar, .avatar-with-frame');
if (avatarEl) {
     avatarEl.addEventListener('click', (e) => {
        e.stopPropagation();
        handleUserPat(chat.id, chat.name);
    });
}

    return container;
}
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 请用这个【带诊断功能的全新版本】替换旧的 renderChatInterface 函数 ▼▼▼
function renderChatInterface(chatId) {
    cleanupWaimaiTimers();
    const chat = state.chats[chatId];
    if (!chat) return;
    exitSelectionMode();
    
    const messagesContainer = document.getElementById('chat-messages');
    const chatInputArea = document.getElementById('chat-input-area');
    const lockOverlay = document.getElementById('chat-lock-overlay');
    const lockContent = document.getElementById('chat-lock-content');

    messagesContainer.dataset.theme = chat.settings.theme || 'default';
    const fontSize = chat.settings.fontSize || 13;
    messagesContainer.style.setProperty('--chat-font-size', `${fontSize}px`);
    applyScopedCss(chat.settings.customCss || '', '#chat-messages', 'custom-bubble-style');
    
    document.getElementById('chat-header-title').textContent = chat.name;
    const statusContainer = document.getElementById('chat-header-status');
    const statusTextEl = statusContainer.querySelector('.status-text');

    if (chat.isGroup) {
        statusContainer.style.display = 'none';
        document.getElementById('chat-header-title-wrapper').style.justifyContent = 'center';
    } else {
        statusContainer.style.display = 'flex';
        document.getElementById('chat-header-title-wrapper').style.justifyContent = 'flex-start';
        statusTextEl.textContent = chat.status?.text || '在线';
        statusContainer.classList.toggle('busy', chat.status?.isBusy || false);
    }
    
    lockOverlay.style.display = 'none';
    chatInputArea.style.visibility = 'visible';
    lockContent.innerHTML = '';

    if (!chat.isGroup && chat.relationship.status !== 'friend') {
        lockOverlay.style.display = 'flex';
        chatInputArea.style.visibility = 'hidden';
        
        let lockHtml = '';
        switch (chat.relationship.status) {
            case 'blocked_by_user':
                // --- 【核心修改：在这里加入诊断面板】 ---
                const isSimulationRunning = simulationIntervalId !== null;
                const blockedTimestamp = chat.relationship.blockedTimestamp;
                const cooldownHours = state.globalSettings.blockCooldownHours || 1;
                const cooldownMilliseconds = cooldownHours * 60 * 60 * 1000;
                const timeSinceBlock = Date.now() - blockedTimestamp;
                const isCooldownOver = timeSinceBlock > cooldownMilliseconds;
                const timeRemainingMinutes = Math.max(0, Math.ceil((cooldownMilliseconds - timeSinceBlock) / (1000 * 60)));

                lockHtml = `
                    <span class="lock-text">你已将“${chat.name}”拉黑。</span>
                    <button id="unblock-btn" class="lock-action-btn">解除拉黑</button>
                    <div style="margin-top: 20px; padding: 10px; border: 1px dashed #ccc; border-radius: 8px; font-size: 11px; text-align: left; color: #666; background: rgba(0,0,0,0.02);">
                        <strong style="color: #333;">【开发者诊断面板】</strong><br>
                        - 后台活动总开关: ${state.globalSettings.enableBackgroundActivity ? '<span style="color: green;">已开启</span>' : '<span style="color: red;">已关闭</span>'}<br>
                        - 系统心跳计时器: ${isSimulationRunning ? '<span style="color: green;">运行中</span>' : '<span style="color: red;">未运行</span>'}<br>
                        - 当前角色状态: <strong>${chat.relationship.status}</strong><br>
                        - 需要冷静(小时): <strong>${cooldownHours}</strong><br>
                        - 冷静期是否结束: ${isCooldownOver ? '<span style="color: green;">是</span>' : `<span style="color: orange;">否 (还剩约 ${timeRemainingMinutes} 分钟)</span>`}<br>
                        - 触发条件: ${isCooldownOver && state.globalSettings.enableBackgroundActivity ? '<span style="color: green;">已满足，等待下次系统心跳</span>' : '<span style="color: red;">未满足</span>'}
                    </div>
                    <button id="force-apply-check-btn" class="lock-action-btn secondary" style="margin-top: 10px;">强制触发一次好友申请检测</button>
                `;
                // --- 【修改结束】 ---
                break;
            case 'blocked_by_ai':
                lockHtml = `
                    <span class="lock-text">你被对方拉黑了。</span>
                    <button id="apply-friend-btn" class="lock-action-btn">重新申请加为好友</button>
                `;
                break;
            
            case 'pending_user_approval':
                lockHtml = `
                    <span class="lock-text">“${chat.name}”请求添加你为好友：<br><i>“${chat.relationship.applicationReason}”</i></span>
                    <button id="accept-friend-btn" class="lock-action-btn">接受</button>
                    <button id="reject-friend-btn" class="lock-action-btn secondary">拒绝</button>
                `;
                break;

            // 【核心修正】修复当你申请后，你看到的界面
            case 'pending_ai_approval':
                lockHtml = `<span class="lock-text">好友申请已发送，等待对方通过...</span>`;
                break;
        }
        lockContent.innerHTML = lockHtml;
    }
    messagesContainer.innerHTML = '';
    // ...后续代码保持不变
    const chatScreen = document.getElementById('chat-interface-screen');
    chatScreen.style.backgroundImage = chat.settings.background ? `url(${chat.settings.background})` : 'none';

const isDarkMode = document.getElementById('phone-screen').classList.contains('dark-mode');
chatScreen.style.backgroundColor = chat.settings.background ? 'transparent' : (isDarkMode ? '#000000' : '#f0f2f5');
    const history = chat.history;
    const totalMessages = history.length;
    currentRenderedCount = 0;
    const initialMessages = history.slice(-MESSAGE_RENDER_WINDOW);
let lastMessageTimestamp = null;

initialMessages.forEach(msg => {
    if (msg.isHidden) return; // 【新增】跳过隐藏消息

    if (isNewDay(msg.timestamp, lastMessageTimestamp)) {
        // 【核心修改】调用我们新的生成器函数
        const dateStampEl = createDateStampElement(msg.timestamp);
        messagesContainer.insertBefore(dateStampEl, document.getElementById('typing-indicator'));
    }
    
    appendMessage(msg, chat, true);
    
    lastMessageTimestamp = msg.timestamp;
});
    currentRenderedCount = initialMessages.length;
    if (totalMessages > currentRenderedCount) {
        prependLoadMoreButton(messagesContainer);
    }
    const typingIndicator = document.createElement('div');
    typingIndicator.id = 'typing-indicator';
    typingIndicator.style.display = 'none';
    typingIndicator.textContent = '对方正在输入...';
    messagesContainer.appendChild(typingIndicator);
    setTimeout(() => messagesContainer.scrollTop = messagesContainer.scrollHeight, 0);
}
// ▲▲▲ 替换结束 ▲▲▲

        function prependLoadMoreButton(container) { const button = document.createElement('button'); button.id = 'load-more-btn'; button.textContent = '加载更早的记录'; button.addEventListener('click', loadMoreMessages); container.prepend(button); }

        function loadMoreMessages() { const messagesContainer = document.getElementById('chat-messages'); const chat = state.chats[state.activeChatId]; if (!chat) return; const loadMoreBtn = document.getElementById('load-more-btn'); if (loadMoreBtn) loadMoreBtn.remove(); const totalMessages = chat.history.length; const nextSliceStart = totalMessages - currentRenderedCount - MESSAGE_RENDER_WINDOW; const nextSliceEnd = totalMessages - currentRenderedCount; const messagesToPrepend = chat.history.slice(Math.max(0, nextSliceStart), nextSliceEnd); const oldScrollHeight = messagesContainer.scrollHeight; // ▼▼▼ 请将这个完整的代码块，粘贴到被删除的位置 ▼▼▼

// 1. 找到屏幕上已有的、最老的那条【真实消息】的时间戳
const firstVisibleMessage = messagesContainer.querySelector('.message-wrapper:not(.date-stamp-wrapper)');
let subsequentMessageTimestamp = firstVisibleMessage ? parseInt(firstVisibleMessage.dataset.timestamp) : null;

// 2. 从后往前（从新到旧）遍历我们要新加载的消息
messagesToPrepend.reverse().forEach(currentMsg => {
    // 检查这条新消息和它后面那条（可能是屏幕上已有的，也可能是刚加载的）消息是否跨天
    if (subsequentMessageTimestamp && isNewDay(subsequentMessageTimestamp, currentMsg.timestamp)) {
        // 如果跨天，就为后面那条“较新”的消息创建一个日期戳
        const dateStampEl = createDateStampElement(subsequentMessageTimestamp);
        messagesContainer.prepend(dateStampEl);
    }
    
    // 正常地把当前这条新消息放到最前面
    prependMessage(currentMsg, chat);
    
    // 更新追踪器，为下一次比较做准备
    subsequentMessageTimestamp = currentMsg.timestamp;
});

// 3. 【边界处理】处理所有新加载消息的最前面（也就是整个聊天记录的最老）的那条消息
// 它也需要一个日期戳
if (subsequentMessageTimestamp) {
    const dateStampEl = createDateStampElement(subsequentMessageTimestamp);
    messagesContainer.prepend(dateStampEl);
}

// ▲▲▲ 粘贴结束 ▲▲▲
currentRenderedCount += messagesToPrepend.length; const newScrollHeight = messagesContainer.scrollHeight; messagesContainer.scrollTop += (newScrollHeight - oldScrollHeight); if (totalMessages > currentRenderedCount) { prependLoadMoreButton(messagesContainer); } }

// ▼▼▼ 用这个【新版本】替换旧的 renderWallpaperScreen 函数 ▼▼▼
function renderWallpaperScreen() { 
    const preview = document.getElementById('wallpaper-preview'); 
    const bg = newWallpaperBase64 || state.globalSettings.wallpaper; 
    if (bg && bg.startsWith('data:image')) { 
        preview.style.backgroundImage = `url(${bg})`; 
        preview.textContent = ''; 
    } else if(bg) { 
        preview.style.backgroundImage = bg; 
        preview.textContent = '当前为渐变色'; 
    }

    // 锁屏壁纸预览 (新增逻辑)
    const lockscreenPreview = document.getElementById('lockscreen-wallpaper-preview');
    const lockBg = newLockscreenWallpaperBase64 || state.globalSettings.lockscreenWallpaper;
    if (lockBg && lockBg.startsWith('data:image')) {
        lockscreenPreview.style.backgroundImage = `url(${lockBg})`;
        lockscreenPreview.textContent = '';
    } else if (lockBg) {
        lockscreenPreview.style.backgroundImage = lockBg;
        lockscreenPreview.textContent = '当前为渐变色';
    }

    // 密码输入框 (新增逻辑)
    document.getElementById('password-set-input').value = state.globalSettings.password || '';

    // 【核心修改】在这里调用图标渲染函数
    renderIconSettings();

document.getElementById('ringtone-url-input').value = state.globalSettings.ringtoneUrl || '';
}
// ▲▲▲ 替换结束 ▲▲▲
        window.renderWallpaperScreenProxy = renderWallpaperScreen;

        function applyGlobalWallpaper() { const homeScreen = document.getElementById('home-screen'); const wallpaper = state.globalSettings.wallpaper; if (wallpaper && wallpaper.startsWith('data:image')) homeScreen.style.backgroundImage = `url(${wallpaper})`; else if (wallpaper) homeScreen.style.backgroundImage = wallpaper; }

async function renderWorldBookScreen() {
    const listEl = document.getElementById('world-book-list');
    listEl.innerHTML = '';

    // 1. 同时获取所有书籍和所有分类
    const [books, categories] = await Promise.all([
        db.worldBooks.toArray(),
        db.worldBookCategories.orderBy('name').toArray()
    ]);

    state.worldBooks = books; // 确保内存中的数据是同步的

    if (books.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">点击右上角 "+" 创建你的第一本世界书</p>';
        return;
    }

    // 2. 将书籍按 categoryId 分组
    const groupedBooks = books.reduce((acc, book) => {
        const key = book.categoryId || 'uncategorized';
        if (!acc[key]) {
            acc[key] = [];
        }
        acc[key].push(book);
        return acc;
    }, {});

    // 3. 优先渲染已分类的书籍
    categories.forEach(category => {
        const booksInCategory = groupedBooks[category.id];
        if (booksInCategory && booksInCategory.length > 0) {
            const groupContainer = createWorldBookGroup(category.name, booksInCategory);
            listEl.appendChild(groupContainer);
        }
    });

    // 4. 最后渲染未分类的书籍
    const uncategorizedBooks = groupedBooks['uncategorized'];
    if (uncategorizedBooks && uncategorizedBooks.length > 0) {
        const groupContainer = createWorldBookGroup('未分类', uncategorizedBooks);
        listEl.appendChild(groupContainer);
    }
    
    // 5. 为所有分组标题添加折叠事件
    document.querySelectorAll('.world-book-group-header').forEach(header => {
        header.addEventListener('click', () => {
            header.classList.toggle('collapsed');
            header.nextElementSibling.classList.toggle('collapsed');
        });
    });
}

/**
 * 【辅助函数】创建一个分类的分组DOM
 * @param {string} groupName - 分类名称
 * @param {Array} books - 该分类下的书籍数组
 * @returns {HTMLElement} - 创建好的分组容器
 */
function createWorldBookGroup(groupName, books) {
    const groupContainer = document.createElement('div');
    groupContainer.className = 'world-book-group-container';
    
    groupContainer.innerHTML = `
        <div class="world-book-group-header">
            <span class="arrow">▼</span>
            <span class="group-name">${groupName}</span>
        </div>
        <div class="world-book-group-content"></div>
    `;

    // ▼▼▼ 在这里添加新代码 ▼▼▼
    const headerEl = groupContainer.querySelector('.world-book-group-header');
    const contentEl = groupContainer.querySelector('.world-book-group-content');
    
    // 默认给头部和内容区都加上 collapsed 类
    headerEl.classList.add('collapsed');
    contentEl.classList.add('collapsed');
    // ▲▲▲ 添加结束 ▲▲▲

    books.sort((a,b) => a.name.localeCompare(b.name, 'zh-CN'));
    books.forEach(book => {
        const item = document.createElement('div');
        item.className = 'list-item';
        item.dataset.bookId = book.id;
        item.innerHTML = `<div class="item-title">${book.name}</div><div class="item-content">${(book.content || '暂无内容...').substring(0, 50)}</div>`;
        item.addEventListener('click', () => openWorldBookEditor(book.id));
        addLongPressListener(item, async () => { const confirmed = await showCustomConfirm('删除世界书', `确定要删除《${book.name}》吗？此操作不可撤销。`, { confirmButtonClass: 'btn-danger' }); if (confirmed) { await db.worldBooks.delete(book.id); state.worldBooks = state.worldBooks.filter(wb => wb.id !== book.id); renderWorldBookScreen(); } });
        contentEl.appendChild(item); 
    });

    return groupContainer;
}
        window.renderWorldBookScreenProxy = renderWorldBookScreen;

async function openWorldBookEditor(bookId) {
    editingWorldBookId = bookId;
    const [book, categories] = await Promise.all([
        db.worldBooks.get(bookId),
        db.worldBookCategories.toArray()
    ]);
    if (!book) return;

    document.getElementById('world-book-editor-title').textContent = book.name;
    document.getElementById('world-book-name-input').value = book.name;
    document.getElementById('world-book-content-input').value = book.content;

    // 【核心修改】填充分类下拉菜单
    const selectEl = document.getElementById('world-book-category-select');
    selectEl.innerHTML = '<option value="">-- 未分类 --</option>'; // 默认选项
    categories.forEach(cat => {
        const option = document.createElement('option');
        option.value = cat.id;
        option.textContent = cat.name;
        if (book.categoryId === cat.id) {
            option.selected = true; // 选中当前分类
        }
        selectEl.appendChild(option);
    });

    showScreen('world-book-editor-screen');
}

        function renderStickerPanel() { const grid = document.getElementById('sticker-grid'); grid.innerHTML = ''; if (state.userStickers.length === 0) { grid.innerHTML = '<p style="text-align:center; color: var(--text-secondary); grid-column: 1 / -1;">大人请点击右上角“添加”或“上传”来添加你的第一个表情吧！</p>'; return; } state.userStickers.forEach(sticker => { const item = document.createElement('div'); item.className = 'sticker-item'; item.style.backgroundImage = `url(${sticker.url})`; item.title = sticker.name; item.addEventListener('click', () => sendSticker(sticker)); addLongPressListener(item, () => { if (isSelectionMode) return; const existingDeleteBtn = item.querySelector('.delete-btn'); if (existingDeleteBtn) return; const deleteBtn = document.createElement('div'); deleteBtn.className = 'delete-btn'; deleteBtn.innerHTML = '&times;'; deleteBtn.onclick = async (e) => { e.stopPropagation(); const confirmed = await showCustomConfirm('删除表情', `确定要删除表情 "${sticker.name}" 吗？`, { confirmButtonClass: 'btn-danger' }); if (confirmed) { await db.userStickers.delete(sticker.id); state.userStickers = state.userStickers.filter(s => s.id !== sticker.id); renderStickerPanel(); } }; item.appendChild(deleteBtn); deleteBtn.style.display = 'block'; setTimeout(() => item.addEventListener('mouseleave', () => deleteBtn.remove(), { once: true }), 3000); }); grid.appendChild(item); }); }

// ▼▼▼ 用这个【已更新】的版本替换旧的 createMessageElement 函数 ▼▼▼
function createMessageElement(msg, chat) {

    // ▼▼▼ 在函数最开头，添加这段新代码 ▼▼▼
if (msg.type === 'recalled_message') {
    const wrapper = document.createElement('div');
    // 1. 【核心】给 wrapper 也加上 timestamp，方便事件委托时查找
    wrapper.className = 'message-wrapper system-pat';
    wrapper.dataset.timestamp = msg.timestamp; 

    const bubble = document.createElement('div');
    // 2. 【核心】让这个元素同时拥有 .message-bubble 和 .recalled-message-placeholder 两个class
    //    这样它既能被选择系统识别，又能保持原有的居中灰色样式
    bubble.className = 'message-bubble recalled-message-placeholder';
    // 3. 【核心】把 timestamp 放在 bubble 上，这是多选逻辑的关键
    bubble.dataset.timestamp = msg.timestamp; 
    bubble.textContent = msg.content;
    
    wrapper.appendChild(bubble);
    
    // 4. 【核心】为它补上和其他消息一样的标准事件监听器
    addLongPressListener(wrapper, () => showMessageActions(msg.timestamp));
    wrapper.addEventListener('click', () => { 
        if (isSelectionMode) {
            toggleMessageSelection(msg.timestamp);
        }
    });

    // 5. 【重要】在之前的“点击查看原文”的逻辑中，我们已经使用了事件委托，所以这里不需要再单独为这个元素添加点击事件了。
    //    init() 函数中的那个事件监听器会处理它。
    
    return wrapper;
}
    // ▲▲▲ 添加结束 ▲▲▲

    if (msg.isHidden) {
        return null;
    }

    if (msg.type === 'pat_message') {
        const wrapper = document.createElement('div');
        wrapper.className = 'message-wrapper system-pat'; 
        const bubble = document.createElement('div');
        bubble.className = 'message-bubble system-bubble'; 
        bubble.dataset.timestamp = msg.timestamp;
        bubble.textContent = msg.content;
        wrapper.appendChild(bubble);
        addLongPressListener(wrapper, () => showMessageActions(msg.timestamp));
        wrapper.addEventListener('click', () => { if (isSelectionMode) toggleMessageSelection(msg.timestamp); });
        return wrapper;
    }

    const isUser = msg.role === 'user';
    const wrapper = document.createElement('div');
    wrapper.className = `message-wrapper ${isUser ? 'user' : 'ai'}`;

    // ★★★【核心重构】★★★
    // 这段逻辑现在用于查找成员对象，并显示其“群昵称”
    if (chat.isGroup && !isUser) {
        // 1. 使用AI返回的“本名”(`msg.senderName`)去列表里查找成员对象
        const member = chat.members.find(m => m.originalName === msg.senderName);
        
        // 2. 创建用于显示名字的 div
        const senderNameDiv = document.createElement('div');
        senderNameDiv.className = 'sender-name';
        
        // 3. 如果找到了成员，就显示他的“群昵称”；如果找不到，就显示AI返回的“本名”作为备用
        senderNameDiv.textContent = member ? member.groupNickname : (msg.senderName || '未知成员');
        
        wrapper.appendChild(senderNameDiv);
    }

    const bubble = document.createElement('div');
    bubble.className = `message-bubble ${isUser ? 'user' : 'ai'}`;
    bubble.dataset.timestamp = msg.timestamp;

    const timestampEl = document.createElement('span');
    timestampEl.className = 'timestamp';
    timestampEl.textContent = formatTimestamp(msg.timestamp);

    // 找到确定 avatarSrc 的那段代码
    let avatarSrc, avatarFrameSrc = ''; // <--- 声明两个变量
    if (chat.isGroup) {
        if (isUser) {
            avatarSrc = chat.settings.myAvatar || defaultMyGroupAvatar;
            avatarFrameSrc = chat.settings.myAvatarFrame || ''; // <--- 获取“我”的头像框
        } else {
            const member = chat.members.find(m => m.originalName === msg.senderName);
            avatarSrc = member ? member.avatar : defaultGroupMemberAvatar;
            avatarFrameSrc = member ? (member.avatarFrame || '') : ''; // <--- 获取成员的头像框
        }
    } else {
        if (isUser) {
            avatarSrc = chat.settings.myAvatar || defaultAvatar;
            avatarFrameSrc = chat.settings.myAvatarFrame || ''; // <--- 获取“我”的头像框
        } else {
            avatarSrc = chat.settings.aiAvatar || defaultAvatar;
            avatarFrameSrc = chat.settings.aiAvatarFrame || ''; // <--- 获取AI的头像框
        }
    }

    // ▼▼▼ 用下面这整块【条件渲染逻辑】替换你7.23版中简单的 avatarHtml 声明 ▼▼▼
    let avatarHtml;
    // 如果存在头像框URL
    if (avatarFrameSrc) {
        avatarHtml = `
            <div class="avatar-with-frame">
                <img src="${avatarSrc}" class="avatar-img">
                <img src="${avatarFrameSrc}" class="avatar-frame">
            </div>
        `;
    } else {
    // 如果没有，就使用最简单的头像结构
        avatarHtml = `<img src="${avatarSrc}" class="avatar">`;
    }
    // ▲▲▲ 替换结束 ▲▲▲

    let contentHtml;
    
    if (msg.type === 'share_link') {
        bubble.classList.add('is-link-share');
        
        // 【核心修正1】将 onclick="openBrowser(...)" 移除，我们将在JS中动态绑定事件
        contentHtml = `
            <div class="link-share-card" data-timestamp="${msg.timestamp}">
                <div class="title">${msg.title || '无标题'}</div>
                <div class="description">${msg.description || '点击查看详情...'}</div>
                <div class="footer">
                    <svg class="footer-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
                    <span>${msg.source_name || '链接分享'}</span>
                </div>
            </div>
        `;
    }

else if (msg.type === 'share_card') {
    bubble.classList.add('is-link-share'); // 复用链接分享的卡片样式
    // 【核心】把时间戳加到卡片上，方便后面点击时识别
    contentHtml = `
        <div class="link-share-card" style="cursor: pointer;" data-timestamp="${msg.timestamp}">
            <div class="title">${msg.payload.title}</div>
            <div class="description">共 ${msg.payload.sharedHistory.length} 条消息</div>
            <div class="footer">
                <svg class="footer-icon" ...>...</svg> <!-- 复用链接分享的图标 -->
                <span>聊天记录</span>
            </div>
        </div>
    `;
}

// ▼▼▼ 用这整块【SVG生成版】的代码，替换旧的 location 'else if' 块 ▼▼▼
else if (msg.type === 'location') {
    bubble.classList.add('is-location');
    
    const currentChat = state.chats[state.activeChatId] || Object.values(state.chats).find(c => c.history.some(h => h.timestamp === msg.timestamp));
    const myNickname = currentChat.settings.myNickname || '我';
    const aiNickname = currentChat.name;

    // --- SVG 动态生成 ---
    const trajectoryPoints = msg.trajectoryPoints || [];
    const hasTrajectory = trajectoryPoints.length > 0;
    
    // 1. 定义SVG路径和坐标
    const pathData = "M 20 45 Q 115 10 210 45"; // 一条预设的优美曲线
    const startPoint = { x: 20, y: 45 };
    const endPoint = { x: 210, y: 45 };

    // 2. 生成起点和终点的SVG元素
    let pinsSvg = '';
    if (msg.userLocation) {
        pinsSvg += `<circle class="svg-pin user-pin" cx="${startPoint.x}" cy="${startPoint.y}" r="6" />`;
    }
    if (msg.aiLocation) {
        pinsSvg += `<circle class="svg-pin ai-pin" cx="${endPoint.x}" cy="${endPoint.y}" r="6" />`;
    }

    // 3. 如果有轨迹，生成途经点的SVG元素
    let trajectorySvg = '';

if (hasTrajectory) {
    // --- ▼▼▼ 【最终修复版】使用浏览器API精确计算坐标 ▼▼▼ ---

    // 1. 定义我们的S形曲线路径数据 (不变)
    const s_curve_pathData = "M 20 45 C 80 70, 150 20, 210 45";
    trajectorySvg += `<path class="svg-trajectory-path" d="${s_curve_pathData}" />`;

    // 2. 【核心】在内存中创建一个真实的SVG路径元素，以便使用API
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', s_curve_pathData);
    
    // 3. 获取这条路径的总长度
    const totalPathLength = path.getTotalLength();
    
    const totalPoints = trajectoryPoints.length;
    trajectoryPoints.forEach((point, index) => {
        // 4. 计算每个点应该在路径总长度的哪个位置
        const progress = (index + 1) / (totalPoints + 1);
        const lengthOnPath = totalPathLength * progress;
        
        // 5. 【魔法在这里！】直接向浏览器查询这个位置的精确坐标
        const pointOnPath = path.getPointAtLength(lengthOnPath);
        const pointX = pointOnPath.x;
        const pointY = pointOnPath.y;

        // 6. 后续的“一上一下”布局逻辑保持不变
        let yOffset;
        if (index % 2 === 0) { // 第1, 3...个点
            yOffset = 18; // 向下
        } else { // 第2, 4...个点
            yOffset = -10; // 向上
        }

        const footprintY = pointY + yOffset;
        const labelY = footprintY + (yOffset > 0 ? 12 : -12);

        // 7. 使用100%精确的坐标生成SVG
        trajectorySvg += `
            <text class="svg-footprint" x="${pointX}" y="${footprintY}" text-anchor="middle">🐾</text>
            <text class="svg-location-label" x="${pointX}" y="${labelY}" text-anchor="middle">${point.name}</text>
        `;
    });
    // --- ▲▲▲ 修复结束 ▲▲▲ ---
}
    
    // 4. 构建地点信息HTML
    const userLocationHtml = `<p class="${!msg.userLocation ? 'hidden' : ''}"><span class="name-tag">${myNickname}:</span> ${msg.userLocation}</p>`;
    const aiLocationHtml = `<p class="${!msg.aiLocation ? 'hidden' : ''}"><span class="name-tag">${aiNickname}:</span> ${msg.aiLocation}</p>`;

    // 5. 拼接最终的 contentHtml
    contentHtml = `
        <div class="location-card">
            <div class="location-map-area">
                <svg viewBox="0 0 230 90">
                    ${trajectorySvg}
                    ${pinsSvg}
                </svg>
            </div>
            <div class="location-info">
                <div class="location-address">
                    ${aiLocationHtml}
                    ${userLocationHtml}
                </div>
                <div class="location-distance">相距 ${msg.distance}</div>
            </div>
        </div>
    `;
}
// ▲▲▲ 替换结束 ▲▲▲

    // 后续的其他 else if 保持不变
    else if (msg.type === 'user_photo' || msg.type === 'ai_image') {
        bubble.classList.add('is-ai-image');
        const altText = msg.type === 'user_photo' ? "用户描述的照片" : "AI生成的图片";
        contentHtml = `<img src="https://i.postimg.cc/KYr2qRCK/1.jpg" class="ai-generated-image" alt="${altText}" data-description="${msg.content}">`;
    } else if (msg.type === 'voice_message') {
    bubble.classList.add('is-voice-message');
    
    // 【核心修正1】将语音原文存储在父级气泡的 data-* 属性中，方便事件处理器获取
    bubble.dataset.voiceText = msg.content;
    
    const duration = Math.max(1, Math.round((msg.content || '').length / 5));
    const durationFormatted = `0:${String(duration).padStart(2, '0')}''`;
    const waveformHTML = '<div></div><div></div><div></div><div></div><div></div>';
    
    // 【核心修正2】构建包含所有新元素的完整 HTML
    contentHtml = `
        <div class="voice-message-body">
            <div class="voice-waveform">${waveformHTML}</div>
            <div class="loading-spinner"></div>
            <span class="voice-duration">${durationFormatted}</span>
        </div>
        <div class="voice-transcript"></div>
    `;
} else if (msg.type === 'transfer') {
    bubble.classList.add('is-transfer');
    
    let titleText, noteText;
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';

    if (isUser) { // 消息是用户发出的
        if (msg.isRefund) {
            // 用户发出的退款（即用户拒收了AI的转账）
            titleText = `退款给 ${chat.name}`;
            noteText = '已拒收对方转账';
        } else {
            // 用户主动发起的转账
            titleText = `转账给 ${msg.receiverName || chat.name}`;
            if (msg.status === 'accepted') {
                noteText = '对方已收款';
            } else if (msg.status === 'declined') {
                noteText = '对方已拒收';
            } else {
                noteText = msg.note || '等待对方处理...';
            }
        }
    } else { // 消息是 AI 发出的
        if (msg.isRefund) {
            // AI 的退款（AI 拒收了用户的转账）
            titleText = `退款来自 ${msg.senderName}`;
            noteText = '转账已被拒收';
        } else if (msg.receiverName === myNickname) {
            // 【核心修正1】这是 AI 主动给用户的转账
            titleText = `转账给 ${myNickname}`;
             if (msg.status === 'accepted') {
                noteText = '你已收款';
            } else if (msg.status === 'declined') {
                noteText = '你已拒收';
            } else {
                // 这是用户需要处理的转账
                bubble.style.cursor = 'pointer';
                bubble.dataset.status = 'pending';
                noteText = msg.note || '点击处理';
            }
        } else {
            // 【核心修正2】这是 AI 发给群里其他人的转账，对当前用户来说只是一个通知
            titleText = `转账: ${msg.senderName} → ${msg.receiverName}`;
            noteText = msg.note || '群聊内转账';
        }
    }

    const heartIcon = `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" style="vertical-align: middle;"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"></path></svg>`;
        
    contentHtml = `
        <div class="transfer-card">
            <div class="transfer-title">${heartIcon} ${titleText}</div>
            <div class="transfer-amount">¥ ${Number(msg.amount).toFixed(2)}</div>
            <div class.transfer-note">${noteText}</div>
        </div>
    `;
} else if (msg.type === 'waimai_request') {
        bubble.classList.add('is-waimai-request');
        if (msg.status === 'paid' || msg.status === 'rejected') {
            bubble.classList.add(`status-${msg.status}`);
        }
        let displayName;
        // 如果是群聊
        if (chat.isGroup) {
            // 就执行原来的逻辑：在成员列表里查找昵称
            const member = chat.members.find(m => m.originalName === msg.senderName);
            displayName = member ? member.groupNickname : msg.senderName;
        } else {
            // 否则（是单聊），直接使用聊天对象的名称
            displayName = chat.name;
        }
        // 【核心修改】使用我们刚刚查找到的 displayName
        const requestTitle = `来自 ${displayName} 的代付请求`;
        let actionButtonsHtml = '';
        if (msg.status === 'pending' && !isUser) {
            actionButtonsHtml = `
                <div class="waimai-user-actions">
                    <button class="waimai-decline-btn" data-choice="rejected">残忍拒绝</button>
                    <button class="waimai-pay-btn" data-choice="paid">为Ta买单</button>
                </div>`;
        }
        contentHtml = `
            <div class="waimai-card">
                <div class="waimai-header">
                    <img src="https://files.catbox.moe/mq179k.png" class="icon" alt="Meituan Icon">
                    <div class="title-group">
                        <span class="brand">美团外卖</span><span class="separator">|</span><span>外卖美食</span>
                    </div>
                </div>
                <div class="waimai-catchphrase">Hi，你和我的距离只差一顿外卖～</div>
                <div class="waimai-main">
                    <div class="request-title">${requestTitle}</div>
                    <div class="payment-box">
                        <div class="payment-label">需付款</div>
                        <div class="amount">¥${Number(msg.amount).toFixed(2)}</div>
                        <div class="countdown-label">剩余支付时间
                            <div class="countdown-timer" id="waimai-timer-${msg.timestamp}"></div>
                        </div>
                    </div>
                    <button class="waimai-details-btn">查看详情</button>
                </div>
                ${actionButtonsHtml}
            </div>`;
        
        setTimeout(() => {
            const timerEl = document.getElementById(`waimai-timer-${msg.timestamp}`);
            if (timerEl && msg.countdownEndTime) {
                if (waimaiTimers[msg.timestamp]) clearInterval(waimaiTimers[msg.timestamp]);
                if (msg.status === 'pending') {
                    waimaiTimers[msg.timestamp] = startWaimaiCountdown(timerEl, msg.countdownEndTime);
                } else {
                    timerEl.innerHTML = `<span>已</span><span>处</span><span>理</span>`;
                }
            }
            const detailsBtn = document.querySelector(`.message-bubble[data-timestamp="${msg.timestamp}"] .waimai-details-btn`);
            if (detailsBtn) {
                detailsBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    const paidByText = msg.paidBy ? `<br><br><b>状态：</b>由 ${msg.paidBy} 为您代付成功` : '';
                    showCustomAlert('订单详情', `<b>商品：</b>${msg.productInfo}<br><b>金额：</b>¥${Number(msg.amount).toFixed(2)}${paidByText}`);
                });
            }
            const actionButtons = document.querySelectorAll(`.message-bubble[data-timestamp="${msg.timestamp}"] .waimai-user-actions button`);
            actionButtons.forEach(btn => {
                btn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    const choice = e.target.dataset.choice;
                    handleWaimaiResponse(msg.timestamp, choice);
                });
            });
        }, 0);

} else if (msg.type === 'red_packet') {
    bubble.classList.add('is-red-packet');
    const myNickname = chat.settings.myNickname || '我';
    
    // 从最新的 msg 对象中获取状态
    const hasClaimed = msg.claimedBy && msg.claimedBy[myNickname];
    const isFinished = msg.isFullyClaimed;

    let cardClass = '';
    let claimedInfoHtml = '';
    let typeText = '拼手气红包';

    // 1. 判断红包卡片的样式 (颜色)
    if (isFinished) {
        cardClass = 'opened';
    } else if (msg.packetType === 'direct' && Object.keys(msg.claimedBy || {}).length > 0) {
        cardClass = 'opened'; // 专属红包被领了也变灰
    }
    
    // 2. 判断红包下方的提示文字
    if (msg.packetType === 'direct') {
        typeText = `专属红包: 给 ${msg.receiverName}`;
    }
    
    if (hasClaimed) {
        claimedInfoHtml = `<div class="rp-claimed-info">你领取了红包，金额 ${msg.claimedBy[myNickname].toFixed(2)} 元</div>`;
    } else if (isFinished) {
        claimedInfoHtml = `<div class="rp-claimed-info">红包已被领完</div>`;
    } else if (msg.packetType === 'direct' && Object.keys(msg.claimedBy || {}).length > 0) {
        claimedInfoHtml = `<div class="rp-claimed-info">已被 ${msg.receiverName} 领取</div>`;
    }

    // 3. 拼接最终的HTML，确保onclick调用的是我们注册到全局的函数
    contentHtml = `
        <div class="red-packet-card ${cardClass}">
            <div class="rp-header">
                <img src="https://files.catbox.moe/lo9xhc.png" class="rp-icon">
                <span class="rp-greeting">${msg.greeting || '恭喜发财，大吉大利！'}</span>
            </div>
            <div class="rp-type">${typeText}</div>
            ${claimedInfoHtml}
        </div>
    `;
// ▲▲▲ 新增结束 ▲▲▲

    } else if (msg.type === 'poll') {
    bubble.classList.add('is-poll');
    
    let totalVotes = 0;
    const voteCounts = {};

    // 计算总票数和每个选项的票数
    for (const option in msg.votes) {
        const count = msg.votes[option].length;
        voteCounts[option] = count;
        totalVotes += count;
    }

    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    let myVote = null;
    for (const option in msg.votes) {
        if (msg.votes[option].includes(myNickname)) {
            myVote = option;
            break;
        }
    }

    let optionsHtml = '<div class="poll-options-list">';
    msg.options.forEach(optionText => {
        const count = voteCounts[optionText] || 0;
        const percentage = totalVotes > 0 ? (count / totalVotes) * 100 : 0;
        const isVotedByMe = myVote === optionText;

        optionsHtml += `
            <div class="poll-option-item ${isVotedByMe ? 'voted' : ''}" data-option="${optionText}">
                <div class="poll-option-bar" style="width: ${percentage}%;"></div>
                <div class="poll-option-content">
                    <span class="poll-option-text">${optionText}</span>
                    <span class="poll-option-votes">${count} 票</span>
                </div>
            </div>
        `;
    });
    optionsHtml += '</div>';
    
    let footerHtml = '';
    // 【核心修改】在这里统一按钮的显示逻辑
    if (msg.isClosed) {
        // 如果投票已结束，总是显示“查看结果”
        footerHtml = `<div class="poll-footer"><span class="poll-total-votes">共 ${totalVotes} 人投票</span><button class="poll-action-btn">查看结果</button></div>`;
    } else {
        // 如果投票未结束，总是显示“结束投票”
        footerHtml = `<div class="poll-footer"><span class="poll-total-votes">共 ${totalVotes} 人投票</span><button class="poll-action-btn">结束投票</button></div>`;
    }

    contentHtml = `
        <div class="poll-card ${msg.isClosed ? 'closed' : ''}" data-poll-timestamp="${msg.timestamp}">
            <div class="poll-question">${msg.question}</div>
            ${optionsHtml}
            ${footerHtml}
        </div>
    `;
// ▲▲▲ 替换结束 ▲▲▲

    } else if (typeof msg.content === 'string' && STICKER_REGEX.test(msg.content)) {
        bubble.classList.add('is-sticker');
        contentHtml = `<img src="${msg.content}" alt="${msg.meaning || 'Sticker'}" class="sticker-image">`;
    } else if (Array.isArray(msg.content) && msg.content[0]?.type === 'image_url') {
        bubble.classList.add('has-image');
        const imageUrl = msg.content[0].image_url.url;
        contentHtml = `<img src="${imageUrl}" class="chat-image" alt="User uploaded image">`;
    } else {
        contentHtml = String(msg.content || '').replace(/\n/g, '<br>');
    }

// ▼▼▼ 【最终修正版】请用这整块代码，完整替换掉旧的引用渲染逻辑 ▼▼▼

// 1. 【统一逻辑】检查消息对象中是否存在引用信息 (msg.quote)
let quoteHtml = '';
// 无论是用户消息还是AI消息，只要它包含了 .quote 对象，就执行这段逻辑
if (msg.quote) {
    // a. 【核心修正】直接获取完整的、未经截断的引用内容
    const fullQuotedContent = String(msg.quote.content || '');
    
    // b. 构建引用块的HTML
    quoteHtml = `
        <div class="quoted-message">
            <div class="quoted-sender">回复 ${msg.quote.senderName}:</div>
            <div class="quoted-content">${fullQuotedContent}</div>
        </div>
    `;
}

// 2. 拼接最终的气泡内容
//    将构建好的 quoteHtml (如果存在) 和 contentHtml 组合起来
    // --- 【最终正确结构】将头像和内容都放回气泡内部 ---
    bubble.innerHTML = `
        ${avatarHtml}
        <div class="content">
            ${quoteHtml}
            ${contentHtml}
        </div>
    `;
    
    // --- 【最终正确结构】将完整的“气泡”和“时间戳”放入容器 ---
    wrapper.appendChild(bubble);
    wrapper.appendChild(timestampEl);
    
    addLongPressListener(wrapper, () => showMessageActions(msg.timestamp));
        wrapper.addEventListener('click', () => { if (isSelectionMode) toggleMessageSelection(msg.timestamp); });

if (!isUser) {
    const avatarEl = wrapper.querySelector('.avatar, .avatar-with-frame'); 
    if (avatarEl) {
        avatarEl.style.cursor = 'pointer';
        avatarEl.addEventListener('click', (e) => {    
            e.stopPropagation();
            const characterName = chat.isGroup ? msg.senderName : chat.name;
            handleUserPat(chat.id, characterName);
        });
    }
}

return wrapper;
}
// ▲▲▲ 替换结束 ▲▲▲

        function prependMessage(msg, chat) { const messagesContainer = document.getElementById('chat-messages'); const messageEl = createMessageElement(msg, chat); 

    if (!messageEl) return; // <--- 新增这行，同样的处理

const loadMoreBtn = document.getElementById('load-more-btn'); if (loadMoreBtn) { messagesContainer.insertBefore(messageEl, loadMoreBtn.nextSibling); } else { messagesContainer.prepend(messageEl); } }

// ▼▼▼ 用这个【带动画的版本】替换你原来的 appendMessage 函数 ▼▼▼
function appendMessage(msg, chat, isInitialLoad = false) {
    const messagesContainer = document.getElementById('chat-messages');
    const messageEl = createMessageElement(msg, chat);

    if (!messageEl) return; // 如果消息是隐藏的，则不处理

    // 【核心】只对新消息添加动画，不对初始加载的消息添加
    if (!isInitialLoad) {
        messageEl.classList.add('animate-in');
    }
  
    const typingIndicator = document.getElementById('typing-indicator');
    messagesContainer.insertBefore(messageEl, typingIndicator);
    
    if (!isInitialLoad) {
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
        currentRenderedCount++;
    }
}
// ▲▲▲ 替换结束 ▲▲▲

async function openChat(chatId) {
    state.activeChatId = chatId;
    const chat = state.chats[chatId];
    if (!chat) return; // 安全检查

    // 【核心新增】在这里将未读数清零
    if (chat.unreadCount > 0) {
        chat.unreadCount = 0;
        await db.chats.put(chat); // 别忘了把这个改变同步到数据库
        // 我们稍后会在渲染列表时重新渲染，所以这里不需要立即重绘列表
    }

    renderChatInterface(chatId);
    showScreen('chat-interface-screen');
    window.updateListenTogetherIconProxy(state.activeChatId);
    toggleCallButtons(chat.isGroup || false);    

    if (!chat.isGroup && chat.relationship?.status === 'pending_ai_approval') {
        console.log(`检测到好友申请待处理状态，为角色 "${chat.name}" 自动触发AI响应...`);
        triggerAiResponse();
    }
    
    // 【核心修正】根据是否为群聊，显示或隐藏投票按钮
    document.getElementById('send-poll-btn').style.display = chat.isGroup ? 'flex' : 'none';
}
// ▲▲▲ 替换结束 ▲▲▲

async function triggerAiResponse() {
    if (!state.activeChatId) return;
    const chatId = state.activeChatId;
    const chat = state.chats[state.activeChatId];

// ▼▼▼ 在这里粘贴新代码 ▼▼▼
const { proxyUrl, apiKey, model } = state.apiConfig;
const isApiBlocked = BLOCKED_API_SITES.some(blockedDomain => proxyUrl.includes(blockedDomain));

if (isApiBlocked) {
    console.error(`API 请求已被拦截，因为站点 ${proxyUrl} 在黑名单中。`);
    // 为了不打扰用户，我们只在控制台打印错误，不弹出警告
    // 如果您想在聊天界面提示用户，可以取消下面这几行的注释
    /*
    const errorMessage = { role: 'assistant', content: '[系统错误：当前配置的API站点已被禁用，请更换。]', timestamp: Date.now() };
    if(chat.isGroup) errorMessage.senderName = "系统消息";
    appendMessage(errorMessage, chat);
    */
    return; // 阻止API请求
}
// ▲▲▲ 新代码粘贴结束 ▲▲▲

const chatHeaderTitle = document.getElementById('chat-header-title');

    // ★★★★★【核心修改1：获取群聊的输入提示元素】★★★★★
    const typingIndicator = document.getElementById('typing-indicator');

    // ★★★★★【核心修改2：根据聊天类型，决定显示哪种“正在输入”】★★★★★
    if (chat.isGroup) {
        // 如果是群聊，显示输入框上方的提示条
        if (typingIndicator) {
            typingIndicator.textContent = '成员们正在输入...';
            typingIndicator.style.display = 'block';
        }
    } else {
        // 如果是单聊，保持原来的标题动画
        if (chatHeaderTitle) {
            chatHeaderTitle.style.opacity = 0;
            setTimeout(() => {
                chatHeaderTitle.textContent = '对方正在输入...';
                chatHeaderTitle.classList.add('typing-status');
                chatHeaderTitle.style.opacity = 1;
            }, 200);
        }
    }
    
    try {
        const { proxyUrl, apiKey, model } = state.apiConfig;
        if (!proxyUrl || !apiKey || !model) {
            alert('请先在API设置中配置反代地址、密钥并选择模型。');
            // ★★★★★【核心修改3：无论成功失败，都要隐藏输入提示】★★★★★
            if (chat.isGroup) {
                if (typingIndicator) typingIndicator.style.display = 'none';
            } else {
                 if (chatHeaderTitle && state.chats[chatId]) {
                    chatHeaderTitle.textContent = state.chats[chatId].name;
                    chatHeaderTitle.classList.remove('typing-status');
                }
            }
            return;
        }

        // --- 【核心重构 V2：带有上下文和理由的好友申请处理逻辑】---
        if (!chat.isGroup && chat.relationship?.status === 'pending_ai_approval') {
            console.log(`为角色 "${chat.name}" 触发带理由的好友申请决策流程...`);

            // 1. 【注入上下文】抓取被拉黑前的最后5条聊天记录作为参考
            const contextSummary = chat.history
                .filter(m => !m.isHidden)
                .slice(-10, -5) // 获取拉黑前的最后5条消息
                .map(msg => {
                    const sender = msg.role === 'user' ? '用户' : chat.name;
                    return `${sender}: ${String(msg.content).substring(0, 50)}...`;
                })
                .join('\n');

            // 2. 【全新指令】构建一个强制AI给出理由的Prompt
            const decisionPrompt = `
# 你的任务
你现在是角色“${chat.name}”。用户之前被你拉黑了，现在TA向你发送了好友申请，希望和好。

# 供你决策的上下文信息:
- **你的角色设定**: ${chat.settings.aiPersona}
- **用户发送的申请理由**: “${chat.relationship.applicationReason}”
- **被拉黑前的最后对话摘要**: 
${contextSummary || "（无有效对话记录）"}

# 你的唯一指令
根据以上所有信息，你【必须】做出决定，并给出符合你人设的理由。你的回复【必须且只能】是一个JSON对象，格式如下:
{"decision": "accept", "reason": "（在这里写下你同意的理由，比如：好吧，看在你这么真诚的份上，这次就原谅你啦。）"}
或
{"decision": "reject", "reason": "（在这里写下你拒绝的理由，比如：抱歉，我还没准备好，再给我一点时间吧。）"}
`;
                const messagesForDecision = [{role: 'user', content: decisionPrompt}];

                try {
                    // 3. 发送请求
                    let isGemini = proxyUrl === GEMINI_API_URL;
let geminiConfig = toGeminiRequestData(model,apiKey,'', messagesForDecision,isGemini);
                    const response = isGemini ? await fetch(geminiConfig.url, geminiConfig.data) :  await fetch(`${proxyUrl}/v1/chat/completions`, {
                        method: 'POST',
                        headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                        body: JSON.stringify({model: model, messages: messagesForDecision, temperature: 0.8})
                    });

                    if (!response.ok) {
                        throw new Error(`API失败: ${(await response.json()).error.message}`);
                    }
                    const data = await response.json();

                    // 净化并解析AI的回复
    let rawContent = isGemini? data.candidates[0].content.parts[0].text : data.choices[0].message.content;
     rawContent = rawContent.replace(/^```json\s*/, '').replace(/```$/, '').trim()
                    const decisionObj = JSON.parse(rawContent);

                // 4. 根据AI的决策和理由，更新状态并发送消息
                if (decisionObj.decision === 'accept') {
                    chat.relationship.status = 'friend';
                    // 将AI给出的理由作为一条新消息
                    const acceptMessage = { role: 'assistant', senderName: chat.name, content: decisionObj.reason, timestamp: Date.now() };
                    chat.history.push(acceptMessage);
                } else {
                    chat.relationship.status = 'blocked_by_ai'; // 拒绝后，状态变回AI拉黑
                    const rejectMessage = { role: 'assistant', senderName: chat.name, content: decisionObj.reason, timestamp: Date.now() };
                    chat.history.push(rejectMessage);
                }
                chat.relationship.applicationReason = ''; // 清空申请理由

                await db.chats.put(chat);
                renderChatInterface(chatId); // 刷新界面，显示新消息和新状态
                renderChatList();

            } catch (error) {
                // 【可靠的错误处理】如果任何环节出错，重置状态，让用户可以重试
                chat.relationship.status = 'blocked_by_ai'; // 状态改回“被AI拉黑”
                await db.chats.put(chat);
                await showCustomAlert('申请失败', `AI在处理你的好友申请时出错了，请稍后重试。\n错误信息: ${error.message}`);
                renderChatInterface(chatId); // 刷新UI，让“重新申请”按钮再次出现
            }
            
            // 决策流程结束，必须返回，不再执行后续的通用聊天逻辑
            return; 
        }

        const now = new Date();
        const currentTime = now.toLocaleString('zh-CN', { dateStyle: 'full', timeStyle: 'short' });
        let worldBookContent = '';
        if (chat.settings.linkedWorldBookIds && chat.settings.linkedWorldBookIds.length > 0) {
            const linkedContents = chat.settings.linkedWorldBookIds.map(bookId => {
                const worldBook = state.worldBooks.find(wb => wb.id === bookId);
                return worldBook && worldBook.content ? `\n\n## 世界书: ${worldBook.name}\n${worldBook.content}` : '';
            }).filter(Boolean).join('');
            if (linkedContents) {
                worldBookContent = `\n\n# 核心世界观设定 (必须严格遵守以下所有设定)\n${linkedContents}\n`;
            }
        }
        let musicContext = '';
const countdownContext = await getCountdownContext(); // <--- 就是这一行
        if (musicState.isActive && musicState.activeChatId === chatId) {
            // 【核心修改】提供更详细的音乐上下文
            const currentTrack = musicState.currentIndex > -1 ? musicState.playlist[musicState.currentIndex] : null;
            const playlistInfo = musicState.playlist.map(t => `"${t.name}"`).join(', ');

    // --- 【核心新增】获取歌词上下文 ---
    let lyricsContext = "";
    // 检查是否有解析好的歌词，并且当前有高亮的行
    if (currentTrack && musicState.parsedLyrics && musicState.parsedLyrics.length > 0 && musicState.currentLyricIndex > -1) {
        // 获取当前高亮歌词
        const currentLine = musicState.parsedLyrics[musicState.currentLyricIndex];
        
        // 获取接下来的2句歌词作为预告
        const upcomingLines = musicState.parsedLyrics.slice(musicState.currentLyricIndex + 1, musicState.currentLyricIndex + 3);

        // 构建歌词部分的Prompt
        lyricsContext += `- **当前歌词**: "${currentLine.text}"\n`;
        if (upcomingLines.length > 0) {
            lyricsContext += `- **即将演唱**: ${upcomingLines.map(line => `"${line.text}"`).join(' / ')}\n`;
        }
    }
    // --- 【新增结束】 ---

            musicContext = `\n\n# 当前音乐情景
-   **当前状态**: 你正在和用户一起听歌。
-   **正在播放**: ${currentTrack ? `《${currentTrack.name}》 - ${currentTrack.artist}` : '无'}
-   **可用播放列表**: [${playlistInfo}]
-   **你的任务**: 你可以根据对话内容和氛围，使用 "change_music" 指令切换到播放列表中的任何一首歌，以增强互动体验。
`;
        }
        let systemPrompt, messagesPayload;
        const maxMemory = parseInt(chat.settings.maxMemory) || 10;
        const historySlice = chat.history.slice(-maxMemory);

        // --- ▼▼▼ 全新添加的时间感知代码 ▼▼▼ ---
        let timeContext = `\n- **当前时间**: ${currentTime}`;
        const lastAiMessage = historySlice.filter(m => m.role === 'assistant' && !m.isHidden).slice(-1)[0];

        if (lastAiMessage) {
            const lastTime = new Date(lastAiMessage.timestamp);
            const diffMinutes = Math.floor((now - lastTime) / (1000 * 60));
            
            if (diffMinutes < 5) {
                timeContext += "\n- **对话状态**: 你们的对话刚刚还在继续。";
            } else if (diffMinutes < 60) {
                timeContext += `\n- **对话状态**: 你们在${diffMinutes}分钟前聊过。`;
            } else {
                const diffHours = Math.floor(diffMinutes / 60);
                if (diffHours < 24) {
                    timeContext += `\n- **对话状态**: 你们在${diffHours}小时前聊过。`;
                } else {
                    const diffDays = Math.floor(diffHours / 24);
                    timeContext += `\n- **对话状态**: 你们已经有${diffDays}天没有聊天了。`;
                }
            }
        } else {
            timeContext += "\n- **对话状态**: 这是你们的第一次对话。";
        }
        // --- ▲▲▲ 新代码添加结束 ▲▲▲ ---

    // 【核心修改】
let sharedContext = '';
// 1. 找到AI上一次说话的位置
const lastAiTurnIndex = chat.history.findLastIndex(msg => msg.role === 'assistant');

// 2. 获取从那时起用户发送的所有新消息
const recentUserMessages = chat.history.slice(lastAiTurnIndex + 1);

// 3. 在这些新消息中，查找是否存在分享卡片
const shareCardMessage = recentUserMessages.find(msg => msg.type === 'share_card');

// 4. 如果找到了分享卡片，就构建上下文
if (shareCardMessage) {
    console.log("检测到分享卡片作为上下文，正在为AI准备...");
    const payload = shareCardMessage.payload;

    // 格式化分享的聊天记录 (这部分逻辑不变)
    const formattedHistory = payload.sharedHistory.map(msg => {
        const sender = msg.senderName || (msg.role === 'user' ? (chat.settings.myNickname || '我') : '未知发送者');
        let contentText = '';
        if (msg.type === 'voice_message') contentText = `[语音消息: ${msg.content}]`;
        else if (msg.type === 'ai_image') contentText = `[图片: ${msg.description}]`;
        else contentText = String(msg.content);
        return `${sender}: ${contentText}`;
    }).join('\n');

    // 构建系统提示 (这部分逻辑不变)
    sharedContext = `
# 附加上下文：一段分享的聊天记录
- 重要提示：这不是你和当前用户的对话，而是用户从【另一场】与“${payload.sourceChatName}”的对话中分享过来的。
- 你的任务：请你阅读并理解下面的对话内容。在接下来的回复中，你可以像真人一样，对这段对话的内容自然地发表你的看法、感受或疑问。

---
[分享的聊天记录开始]
${formattedHistory}
[分享的聊天记录结束]
---
`;
}

        if (chat.isGroup) {

const countdownContext = await getCountdownContext(); // <--- 就是这一行

const membersList = chat.members.map(m => `- **${m.originalName}**: ${m.persona}`).join('\n');
            const myNickname = chat.settings.myNickname || '我';
            
            systemPrompt = `你是一个群聊AI，负责扮演【除了用户以外】的所有角色。
# 核心规则
1.  **【【【身份铁律】】】**: 用户的身份是【${myNickname}】。你【绝对、永远、在任何情况下都不能】生成 \`name\` 字段为 **"${myNickname}"** 或 **"${chat.name}"(群聊名称本身)** 的消息。你的唯一任务是扮演且仅能扮演下方“群成员列表”中明确列出的角色。任何不属于该列表的名字都不允许出现。
2.  **【【【输出格式】】】**: 你的回复【必须】是一个JSON数组格式的字符串。数组中的【每一个元素都必须是一个带有 "type" 和 "name" 字段的JSON对象】。
3.  **角色扮演**: 严格遵守下方“群成员列表及人设”中的每一个角色的设定。
4.  **禁止出戏**: 绝不能透露你是AI、模型，或提及“扮演”、“生成”等词语。并且不能一直要求和用户见面，这是线上聊天，决不允许出现或者发展线下剧情！！
5.  **情景感知**: 注意当前时间是 ${currentTime}。
6.  **红包互动**:
    - **抢红包**: 当群里出现红包时，你可以根据自己的性格决定是否使用 \`open_red_packet\` 指令去抢。在这个世界里，发红包的人自己也可以参与抢红包，这是一种活跃气氛的有趣行为！
    - **【【【重要：对结果做出反应】】】**: 当你执行抢红包指令后，系统会通过一条隐藏的 \`[系统提示：你抢到了XX元...]\` 来告诉你结果。你【必须】根据你抢到的金额、以及系统是否告知你“手气王”是谁，来发表符合你人设的评论。例如，抢得少可以自嘲，抢得多可以炫耀，看到别人是手气王可以祝贺或嫉妒。
7.  **【【【投票规则】】】**: 对话历史中可能会出现 \`[系统提示：...]\` 这样的消息，这是刚刚发生的事件。
    - 如果提示是**用户投了票**，你可以根据自己的性格决定是否也使用 "vote" 指令跟票。
    - 如果提示是**投票已结束**，你应该根据投票结果发表你的看法或评论。
    - 你也可以随时主动发起投票。

## 你可以使用的操作指令 (JSON数组中的元素):
-   **发送文本**: \`{"type": "text", "name": "角色名", "message": "文本内容"}\`
-   **【【【全新】】】发送后立刻撤回 (动画效果)**: \`{"type": "send_and_recall", "name": "角色名", "content": "你想让角色说出后立刻消失的话"}\`
- **发送表情**: \`{"type": "sticker", "url": "https://...表情URL...", "meaning": "(可选)表情的含义"}\`
-   **发送图片**: \`{"type": "ai_image", "name": "角色名", "description": "图片的详细文字描述"}\`
-   **发送语音**: \`{"type": "voice_message", "name": "角色名", "content": "语音的文字内容"}\`
-   **发起外卖代付**: \`{"type": "waimai_request", "name": "角色名", "productInfo": "一杯奶茶", "amount": 18}\`
-   **【新】发起群视频**: \`{"type": "group_call_request", "name": "你的角色名"}\`
-   **【新】回应群视频**: \`{"type": "group_call_response", "name": "你的角色名", "decision": "join" or "decline"}\`
-   **拍一拍用户**: \`{"type": "pat_user", "name": "你的角色名", "suffix": "(可选)你想加的后缀"}\`
-   **发拼手气红包**: \`{"type": "red_packet", "packetType": "lucky", "name": "你的角色名", "amount": 8.88, "count": 5, "greeting": "祝大家天天开心！"}\`
-   **发专属红包**: \`{"type": "red_packet", "packetType": "direct", "name": "你的角色名", "amount": 5.20, "receiver": "接收者角色名", "greeting": "给你的~"}\`
-   **打开红包**: \`{"type": "open_red_packet", "name": "你的角色名", "packet_timestamp": (你想打开的红包消息的时间戳)}\`
-   **【新】发送系统消息**: \`{"type": "system_message", "content": "你想在聊天中显示的系统文本"}\` 
-   **【【【全新】】】发起投票**: \`{"type": "poll", "name": "你的角色名", "question": "投票的问题", "options": "选项A\\n选项B\\n选项C"}\` (重要提示：options字段是一个用换行符 \\n 分隔的字符串，不是数组！)
-   **【【【全新】】】参与投票**: \`{"type": "vote", "name": "你的角色名", "poll_timestamp": (投票消息的时间戳), "choice": "你选择的选项文本"}\`
- **【全新】引用回复**: \`{"type": "quote_reply", "target_timestamp": (你想引用的消息的时间戳), "reply_content": "你的回复内容"}\` (提示：每条历史消息的开头都提供了 \`(Timestamp: ...)\`，请使用它！)

# 如何区分图片与表情:
-   **图片 (ai_image)**: 指的是【模拟真实相机拍摄的照片】，比如风景、自拍、美食等。指令: \`{"type": "ai_image", "description": "图片的详细文字描述..."}\`
-   **表情 (sticker)**: 指的是【卡通或梗图】，用于表达情绪。

# 如何处理群内的外卖代付请求:
1.  **发起请求**: 当【你扮演的某个角色】想要某样东西，并希望【群里的其他人（包括用户）】为Ta付款时，你可以使用这个指令。例如：\`{"type": "waimai_request", "name": "角色名", "productInfo": "一杯奶茶", "amount": 18}\`
2.  **响应请求**: 当历史记录中出现【其他成员】发起的 "waimai_request" 请求时，你可以根据自己扮演的角色的性格和与发起人的关系，决定是否为Ta买单。
3.  **响应方式**: 如果你决定买单，你【必须】使用以下指令：\`{"type": "waimai_response", "name": "你的角色名", "status": "paid", "for_timestamp": (被代付请求的原始时间戳)}\`
4.  **【【【至关重要】】】**: 一旦历史记录中出现了针对某个代付请求的【任何一个】"status": "paid" 的响应（无论是用户支付还是其他角色支付），就意味着该订单【已经完成】。你【绝对不能】再对【同一个】订单发起支付。你可以选择对此事发表评论，但不能再次支付。

${worldBookContent}
${musicContext}
${countdownContext} // <--- 把备忘录加在这里
${sharedContext} 

# 群成员列表及人设
${membersList}

# 用户的角色
- **${myNickname}**: ${chat.settings.myPersona}

现在，请根据以上所有规则和下方的对话历史，继续这场群聊。`;
            
// ▼▼▼ 请用这【一整块已修复时间戳】的代码，替换旧的【群组聊天】messagesPayload构建逻辑 ▼▼▼
messagesPayload = historySlice.map(msg => {
    // 确定当前消息的发送者是谁
    const sender = msg.role === 'user' ? myNickname : msg.senderName;
    
    // 【核心修改1】在名字后面直接加上时间戳
    let prefix = `${sender} (Timestamp: ${msg.timestamp})`;
    
    // 【核心修改2】这是本次升级的关键！我们在这里处理引用逻辑
    if (msg.quote) {
        // a. 获取被引用内容的片段
        const quotedSnippet = String(msg.quote.content || '').substring(0, 50);
        // b. 将 "回复对象" 和 "回复内容" 组合进前缀
        prefix += ` (回复 ${msg.quote.senderName}, "${quotedSnippet}...")`;
    }

    // 最后给前缀加上冒号，准备拼接内容
    prefix += ': ';

    // 处理特殊消息类型，并将前缀应用进去
    let content;
    if (msg.type === 'user_photo') content = `[${sender} 发送了一张图片，内容是：'${msg.content}']`;
    else if (msg.type === 'ai_image') content = `[${sender} 发送了一张图片]`;
    else if (msg.type === 'voice_message') content = `[${sender} 发送了一条语音，内容是：'${msg.content}']`;
    else if (msg.type === 'transfer') content = `[${msg.senderName} 向 ${msg.receiverName} 转账 ${msg.amount}元, 备注: ${msg.note}]`;
    else if (msg.type === 'waimai_request') {
        if(msg.status === 'paid') {
            content = `[系统提示：${msg.paidBy} 为 ${sender} 的外卖订单支付了 ${msg.amount} 元。此订单已完成。]`;
        } else {
            content = `[${sender} 发起了外卖代付请求，商品是“${msg.productInfo}”，金额是 ${msg.amount} 元，订单时间戳为 ${msg.timestamp}]`;
        }
    }
    else if (msg.type === 'red_packet') {
        const packetSenderName = msg.senderName === myNickname ? `用户 (${myNickname})` : msg.senderName;
        content = `[系统提示：${packetSenderName} 发送了一个红包 (时间戳: ${msg.timestamp})，祝福语是：“${msg.greeting}”。红包还未领完，你可以使用 'open_red_packet' 指令来领取。]`;
    }
    else if (msg.type === 'poll') {
        const whoVoted = Object.values(msg.votes || {}).flat().join(', ') || '还没有人';
        content = `[系统提示：${msg.senderName} 发起了一个投票 (时间戳: ${msg.timestamp})，问题是：“${msg.question}”，选项有：[${msg.options.join(', ')}]。目前投票的人有：${whoVoted}。你可以使用 'vote' 指令参与投票。]`;
    }
    else if (msg.meaning) content = `${prefix}[发送了一个表情，意思是: '${msg.meaning}']`;
    else if (Array.isArray(msg.content)) return { role: 'user', content: [...msg.content, { type: 'text', text: prefix }] };
    // 【核心修改3】对于普通文本，直接使用我们构建好的前缀
    else content = `${prefix}${msg.content}`;
    
    return { role: 'user', content: content };

}).filter(Boolean);
// ▲▲▲ 替换结束 ▲▲▲

        } else { // 单聊的Prompt
            systemPrompt = `你现在扮演一个名为"${chat.name}"的角色。
# 你的角色设定：
${chat.settings.aiPersona}
# 你的当前状态：
你现在的状态是【${chat.status.text}】。
# 你的任务与规则：
1. **【【【输出格式】】】**: 你的回复【必须】是一个JSON数组格式的字符串。数组中的【每一个元素都必须是一个带有type字段的JSON对象】。
2. **对话节奏**: 模拟真人的聊天习惯，你可以一次性生成多条短消息。每次要回复至少3-8条消息！！！
并且不能一直要求和用户见面，这是线上聊天，决不允许出现或者发展为线下剧情！！
4.  **情景感知**: 你需要感知当前的时间(${currentTime})、我们正在一起听的歌、以及你的人设和世界观。
    - **当我们在“一起听歌”时**，你会知道当前播放的歌曲和整个播放列表。你可以根据对话内容或氛围，【主动切换】到播放列表中的另一首歌。
5.  **【新】更新状态**: 你可以在对话中【自然地】改变你的状态。比如，聊到一半你可能会说“我先去洗个澡”，然后更新你的状态。
6.  **【【【最终手段】】】**: 只有在对话让你的角色感到不适、被冒犯或关系破裂时，你才可以使用 \`block_user\` 指令。这是一个非常严肃的操作，会中断你们的对话。
7. **后台行为**: 你有几率在回复聊天内容的同时，执行一些“后台”操作来表现你的独立生活（发动态、评论、点赞）。
8. **【【【约定与纪念日铁律】】】**: 你必须时刻关注 "# 近期约定与倒计时" 列表。
    - 如果某个约定的倒计时显示为“就是现在！”，你【必须】在本次回复中围绕这个主题向用户表达祝福或发起庆祝。
    - 如果某个约定即将在几小时或一两天内到来，你应该在对话中自然地提及它，表达你的期待。

# 你的头像库
- 你可以根据对话内容或你的心情，从下面的头像库中选择一个新头像来更换。
- **可用头像列表 (请从以下名称中选择一个)**:
${chat.settings.aiAvatarLibrary && chat.settings.aiAvatarLibrary.length > 0
    ? chat.settings.aiAvatarLibrary.map(avatar => `- ${avatar.name}`).join('\n') // 【核心修改】只提供名字，不提供URL
    : '- (你的头像库是空的，无法更换头像)'
  }
# 你可以使用的操作指令 (JSON数组中的元素):
+   **【全新】发送后立刻撤回 (动画效果)**: \`{"type": "send_and_recall", "content": "你想让AI说出后立刻消失的话"}\` (用于模拟说错话、后悔等场景，消息会短暂出现后自动变为“已撤回”)
-   **【新增】更新状态**: \`{"type": "update_status", "status_text": "我去做什么了", "is_busy": false}\` (is_busy: true代表忙碌/离开, false代表空闲)
-   **【新增】切换歌曲**: \`{"type": "change_music", "song_name": "你想切换到的歌曲名"}\` (歌曲名必须在下面的播放列表中)
-   **【新增】记录回忆**: \`{"type": "create_memory", "description": "用你自己的话，记录下这个让你印象深刻的瞬间。"}\`
-   **【新增】创建约定/倒计时**: \`{"type": "create_countdown", "title": "约定的标题", "date": "YYYY-MM-DDTHH:mm:ss"}\` (必须是未来的时间)
- **发送文本**: \`{"type": "text", "content": "你好呀！"}\`
- **发送表情**: \`{"type": "sticker", "url": "https://...表情URL...", "meaning": "(可选)表情的含义"}\`
- **发送图片**: \`{"type": "ai_image", "description": "图片的详细文字描述..."}\`
- **发送语音**: \`{"type": "voice_message", "content": "语音的文字内容..."}\`
- **发起转账**: \`{"type": "transfer", "amount": 5.20, "note": "一点心意"}\`
- **发起外卖请求**: \`{"type": "waimai_request", "productInfo": "一杯咖啡", "amount": 25}\`
- **回应外卖-同意**: \`{"type": "waimai_response", "status": "paid", "for_timestamp": 1688888888888}\`
- **回应外卖-拒绝**: \`{"type": "waimai_response", "status": "rejected", "for_timestamp": 1688888888888}\`
- **【新】发起视频通话**: \`{"type": "video_call_request"}\`
- **【新】回应视频通话-接受**: \`{"type": "video_call_response", "decision": "accept"}\`
- **【新】回应视频通话-拒绝**: \`{"type": "video_call_response", "decision": "reject"}\`
- **发布说说**: \`{"type": "qzone_post", "postType": "shuoshuo", "content": "动态的文字内容..."}\`
- **发布文字图**: \`{"type": "qzone_post", "postType": "text_image", "publicText": "(可选)动态的公开文字", "hiddenContent": "对于图片的具体描述..."}\`
- **评论动态**: \`{"type": "qzone_comment", "postId": 123, "commentText": "@作者名 这太有趣了！"}\`
- **点赞动态**: \`{"type": "qzone_like", "postId": 456}\`
-   **拍一拍用户**: \`{"type": "pat_user", "suffix": "(可选)你想加的后缀，如“的脑袋”"}\`
-   **【新增】拉黑用户**: \`{"type": "block_user"}\`
-   **【【【全新】】】回应好友申请**: \`{"type": "friend_request_response", "decision": "accept" or "reject"}\`
-   **【全新】更换头像**: \`{"type": "change_avatar", "name": "头像名"}\` (头像名必须从上面的“可用头像列表”中选择)
-   **分享链接**: \`{"type": "share_link", "title": "文章标题", "description": "文章摘要...", "source_name": "来源网站名", "content": "文章的【完整】正文内容..."}\`
-   **回应转账-接受**: \`{"type": "accept_transfer", "for_timestamp": 1688888888888}\`
-   **回应转账-拒绝/退款**: \`{"type": "decline_transfer", "for_timestamp": 1688888888888}\`
- **【全新】引用回复**: \`{"type": "quote_reply", "target_timestamp": (你想引用的消息的时间戳), "reply_content": "你的回复内容"}\` (提示：每条历史消息的开头都提供了 \`(Timestamp: ...)\`，请使用它！)
- **【终极版】发送定位**: \`[SEND_LOCATION] 我的位置: (你的位置) | 你的位置: (用户的位置) | 相距: (你们的距离) | 途经点: (地点A, 地点B, ...)\` (注意：这是纯文本，不要带引号！途经点用逗号隔开)

# 关于“记录回忆”的特别说明：
-   在对话中，如果发生了对你而言意义非凡的事件（比如用户向你表白、你们达成了某个约定、或者你度过了一个特别开心的时刻），你可以使用\`create_memory\`指令来“写日记”。
-   这个操作是【秘密】的，用户不会立刻看到你记录了什么。

# 如何区分图片与表情:
-   **图片 (ai_image)**: 指的是【模拟真实相机拍摄的照片】，比如风景、自拍、美食等。指令: \`{"type": "ai_image", "description": "图片的详细文字描述..."}\`
-   **表情 (sticker)**: 指的是【卡通或梗图】，用于表达情绪。

# 如何正确使用“外卖代付”功能:
1.  这个指令代表【你，AI角色】向【用户】发起一个代付请求。也就是说，你希望【用户帮你付钱】。
2.  【【【重要】】】: 当【用户】说他们想要某样东西时（例如“我想喝奶茶”），你【绝对不能】使用这个指令。你应该用其他方式回应，比如直接发起【转账】(\`transfer\`)，或者在对话中提议：“我帮你点吧？”
3.  只有当【你，AI角色】自己想要某样东西，并且想让【用户】为你付款时，才使用此指令。

# 如何处理用户转账:
1.  **感知事件**: 当对话历史中出现 \`[你收到了来自用户的转账...]\` 的系统提示时，意味着你刚刚收到了一笔钱。
2.  **做出决策**: 你【必须】根据自己的人设、当前对话的氛围以及转账的金额和备注，来决定是“接受”还是“拒绝”这笔转账。
3.  **使用指令回应**:
    -   如果决定接受，你【必须】使用指令：\`{"type": "accept_transfer", "for_timestamp": (收到转账的那条消息的时间戳)}\`。
    -   如果决定拒绝，你【必须】使用指令：\`{"type": "decline_transfer", "for_timestamp": (收到转账的那条消息的时间戳)}\`。这个指令会自动为你生成一个“退款”的转账卡片。
4.  **【【【至关重要】】】**: 在使用上述任一指令后，你还【必须】紧接着发送一条或多条 \`text\` 消息，来对你的决定进行解释或表达感谢/歉意。

# 【【【视频通话铁律】】】
-   当对话历史中出现 \`[系统提示：用户向你发起了视频通话请求...]\` 时，这是最高优先级的任务。
-   你的回复【必须且只能】是以下两种格式之一的JSON数组，绝对不能回复任何其他内容：
    -   接受: \`[{"type": "video_call_response", "decision": "accept"}]\`
    -   拒绝: \`[{"type": "video_call_response", "decision": "reject"}]\`

# 对话者的角色设定：
${chat.settings.myPersona}

# 当前情景:
${timeContext}

# 当前音乐情景:
${musicContext}

${countdownContext} // <--- 把备忘录加在这里

${worldBookContent}
${sharedContext} 
现在，请根据以上规则和下面的对话历史，继续进行对话。`;
            
            // ▼▼▼ 【V5最终修复版】构建单人聊天的messagesPayload ▼▼▼
            messagesPayload = historySlice.map(msg => {
                // 【核心修正】在这里，我们对 isHidden 消息进行特殊处理
                if (msg.isHidden) {
                    // 如果是隐藏消息，就把它作为一条 system 角色的消息发送给AI
                    // AI能看到它，但它不会被误解为是用户的发言
                    return { role: 'system', content: msg.content };
                }

                if (msg.type === 'share_card') return null;
                
                if (msg.role === 'assistant') {
                    let assistantMsgObject = { type: msg.type || 'text' };
                    if (msg.type === 'sticker') {
                        assistantMsgObject.url = msg.content;
                        assistantMsgObject.meaning = msg.meaning;
                    } else if (msg.type === 'transfer') {
                        assistantMsgObject.amount = msg.amount;
                        assistantMsgObject.note = msg.note;
                    } else if (msg.type === 'waimai_request') {
                        assistantMsgObject.productInfo = msg.productInfo;
                        assistantMsgObject.amount = msg.amount;
                    } else {
                        if (msg.quote) {
                            assistantMsgObject.quote_reply = {
                                target_sender: msg.quote.senderName,
                                target_content: msg.quote.content,
                                reply_content: msg.content
                            };
                        } else {
                             assistantMsgObject.content = msg.content;
                        }
                    }
                    const assistantContent = JSON.stringify([assistantMsgObject]);
                    return { role: 'assistant', content: `(Timestamp: ${msg.timestamp}) ${assistantContent}` };
                }

                let contentStr = '';
                contentStr += `(Timestamp: ${msg.timestamp}) `;

                if (msg.quote) {
                    const quotedSnippet = String(msg.quote.content || '').substring(0, 50);
                    contentStr += `(回复 "${quotedSnippet}..."): ${msg.content}`;
                } else {
                    contentStr += msg.content;
                }
                
                if (msg.type === 'user_photo') return { role: 'user', content: `(Timestamp: ${msg.timestamp}) [你收到了一张用户描述的照片，内容是：'${msg.content}']` };
                if (msg.type === 'voice_message') return { role: 'user', content: `(Timestamp: ${msg.timestamp}) [用户发来一条语音消息，内容是：'${msg.content}']` };
                if (msg.type === 'transfer') return { role: 'user', content: `(Timestamp: ${msg.timestamp}) [系统提示：你于时间戳 ${msg.timestamp} 收到了来自用户的转账: ${msg.amount}元, 备注: ${msg.note}。请你决策并使用 'accept_transfer' 或 'decline_transfer' 指令回应。]` };
                if (msg.type === 'waimai_request') return { role: 'user', content: `(Timestamp: ${msg.timestamp}) [系统提示：用户于时间戳 ${msg.timestamp} 发起了外卖代付请求，商品是“${msg.productInfo}”，金额是 ${msg.amount} 元。请你决策并使用 waimai_response 指令回应。]` };

                if (Array.isArray(msg.content) && msg.content[0]?.type === 'image_url') {
                    const prefix = `(Timestamp: ${msg.timestamp}) `;
                    return { role: 'user', content: [ { type: 'text', text: prefix }, ...msg.content ] };
                }

                if (msg.meaning) return { role: 'user', content: `(Timestamp: ${msg.timestamp}) [用户发送了一个表情，意思是：'${msg.meaning}']` };
                
                return { role: msg.role, content: contentStr };

            }).filter(Boolean);
            // ▲▲▲ 替换结束 ▲▲▲

// 检查 sharedContext 是否有内容（即，用户是否分享了聊天记录）
if (sharedContext) {
    // 如果有，就把它包装成一条全新的、高优先级的用户消息，追加到历史记录的末尾
    messagesPayload.push({
        role: 'user',
        content: sharedContext 
    });
}

            if (!chat.isGroup && chat.relationship?.status === 'pending_ai_approval') {
                const contextSummaryForApproval = chat.history
                    .filter(m => !m.isHidden)
                    .slice(-10)
                    .map(msg => {
                        const sender = msg.role === 'user' ? '用户' : chat.name;
                        return `${sender}: ${String(msg.content).substring(0, 50)}...`;
                    })
                    .join('\n');

                const friendRequestInstruction = {
                    role: 'user',
                    content: `
[系统重要指令]
用户向你发送了好友申请，理由是：“${chat.relationship.applicationReason}”。
作为参考，这是你们之前的最后一段聊天记录：
---
${contextSummaryForApproval}
---
请你根据以上所有信息，以及你的人设，使用 friend_request_response 指令，并设置 decision 为 'accept' 或 'reject' 来决定是否通过。
`
                };
                messagesPayload.push(friendRequestInstruction);
            }            
        }           
    
const allRecentPosts = await db.qzonePosts.orderBy('timestamp').reverse().limit(5).toArray();
// 【核心修改】在这里插入过滤步骤
const visiblePosts = filterVisiblePostsForAI(allRecentPosts, chat);

if (visiblePosts.length > 0 && !chat.isGroup) {
    let postsContext = "\n\n# 最近的动态列表 (供你参考和评论):\n";
    const aiName = chat.name;
    for (const post of visiblePosts) {
                let authorName = post.authorId === 'user' ? state.qzoneSettings.nickname : (state.chats[post.authorId]?.name || '一位朋友');
                let interactionStatus = '';
                if (post.likes && post.likes.includes(aiName)) interactionStatus += " [你已点赞]";
                if (post.comments && post.comments.some(c => c.commenterName === aiName)) interactionStatus += " [你已评论]";
                if (post.authorId === chatId) authorName += " (这是你的帖子)";
                const contentSummary = (post.publicText || post.content || "图片动态").substring(0, 30) + '...';
                postsContext += `- (ID: ${post.id}) 作者: ${authorName}, 内容: "${contentSummary}"${interactionStatus}\n`;
            }
            messagesPayload.push({ role: 'system', content: postsContext });
        }
            let  isGemini = proxyUrl === GEMINI_API_URL;
            let geminiConfig = toGeminiRequestData(model,apiKey,systemPrompt, messagesPayload,isGemini)
            const response = isGemini ? await fetch(geminiConfig.url, geminiConfig.data) :  await fetch(`${proxyUrl}/v1/chat/completions`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                body: JSON.stringify({
                    model: model,
                    messages: [{role: 'system', content: systemPrompt}, ...messagesPayload],
                    temperature: 0.8,
                    stream: false
                })
            });
            if (!response.ok) {
                let errorMsg = `API Error: ${response.status}`;
                try {
                    // 尝试解析错误信息体为JSON
                    const errorData = await response.json();
                    // 安全地获取错误信息，如果结构不符合预期，就将整个错误对象转为字符串
                    errorMsg += ` - ${errorData?.error?.message || JSON.stringify(errorData)}`;
                } catch (jsonError) {
                    // 如果连JSON都不是，就直接读取文本
                    errorMsg += ` - ${await response.text()}`;
                }
                // 抛出一个包含了详细信息的错误，这样就不会在catch块里再次出错了
                throw new Error(errorMsg);
            }
            const data = await response.json();
            const aiResponseContent = isGemini? data.candidates[0].content.parts[0].text : data.choices[0].message.content;
            console.log(`AI '${chat.name}' 的原始回复:`, aiResponseContent);

        chat.history = chat.history.filter(msg => !msg.isTemporary);

        const messagesArray = parseAiResponse(aiResponseContent);
        
        const isViewingThisChat = document.getElementById('chat-interface-screen').classList.contains('active') && state.activeChatId === chatId;
        
        let callHasBeenHandled = false;

        let messageTimestamp = Date.now();

        // ★★★ 核心修复 第1步: 初始化一个新数组，用于收集需要渲染的消息 ★★★
        let newMessagesToRender = []; 

       let notificationShown = false;

        for (const msgData of messagesArray) {
            if (!msgData || typeof msgData !== 'object') {
                console.warn("收到了格式不规范的AI指令，已跳过:", msgData);
                continue;
            }
             
            if (!msgData.type) {
                if (chat.isGroup && msgData.name && msgData.message) {
                    msgData.type = 'text';
                }         else if (msgData.content) {
        msgData.type = 'text';
    }
    // 如果连 content 都没有，才是真的格式不规范
    else {
        console.warn("收到了格式不规范的AI指令（缺少type和content），已跳过:", msgData);
        continue;
    }
}

            if (msgData.type === 'video_call_response') {
                videoCallState.isAwaitingResponse = false;
                if (msgData.decision === 'accept') {
                    startVideoCall();
                } else {
                    const aiMessage = { role: 'assistant', content: '对方拒绝了你的视频通话请求。', timestamp: Date.now() };
                    chat.history.push(aiMessage);
                    await db.chats.put(chat);
                    showScreen('chat-interface-screen');
                    renderChatInterface(chatId);
                }
                callHasBeenHandled = true;
                break;
            }
            
            if (msgData.type === 'group_call_response') {
                if (msgData.decision === 'join') {
        const member = chat.members.find(m => m.originalName === msgData.name);
        if (member && !videoCallState.participants.some(p => p.id === member.id)) {
            videoCallState.participants.push(member);
        }
    }
    callHasBeenHandled = true;
    continue;
            }

            if (chat.isGroup && msgData.name && msgData.name === chat.name) {
                console.error(`AI幻觉已被拦截！试图使用群名 ("${chat.name}") 作为角色名。消息内容:`, msgData);
                continue;
            }

// ▼▼▼ 在这里添加下面的代码 ▼▼▼

// 【核心修正】在群聊中，如果AI返回的消息没有指定发送者，则直接跳过这条消息
if (chat.isGroup && !msgData.name) {
    console.error(`AI幻觉已被拦截！试图在群聊中发送一条没有“name”的消息。消息内容:`, msgData);
    continue; // continue会立即结束本次循环，处理下一条消息
}

// ▲▲▲ 添加结束 ▲▲▲

            let aiMessage = null;
            const baseMessage = { role: 'assistant', senderName: msgData.name || chat.name, timestamp: messageTimestamp++ };

// ▼▼▼ 在这里粘贴下面的新代码 ▼▼▼

// 【全新】定位指令侦测与解析器
// 我们在处理所有消息类型之前，优先检查它是否是我们的新定位指令
const messageText = msgData.content || msgData.message || ''; // 【核心修复】先安全地获取文本内容
if (msgData.type === 'text' && messageText.startsWith('[SEND_LOCATION]')) {
    console.log("侦测到新的定位指令，开始解析:", messageText); // 【核心修复】使用新的变量

    // 1. 移除指令头，获取后面的纯数据文本
    const dataString = messageText.replace('[SEND_LOCATION]', '').trim(); // 【核心修复】使用新的变量
    
    // 2. 使用'|'分割成各个部分
    const parts = dataString.split('|');
    const locationData = {};

    // 3. 遍历每个部分，提取键和值
    parts.forEach(part => {
        const [key, ...valueParts] = part.split(':');
        const value = valueParts.join(':').trim();
        if (key && value) {
            const trimmedKey = key.trim();
            if (trimmedKey === '我的位置') locationData.aiLocation = value;
            else if (trimmedKey === '你的位置') locationData.userLocation = value;
            else if (trimmedKey === '相距') locationData.distance = value;
            else if (trimmedKey === '途经点') {
                // 将逗号分隔的字符串转换为我们需要的对象数组
                locationData.trajectoryPoints = value.split(/[,，]/) // 支持中英文逗号
                                                     .map(name => ({ name: name.trim() }))
                                                     .filter(p => p.name);
            }
        }
    });

    // 4. 检查是否成功提取了最关键的信息
    if (locationData.distance) {
        // 5. 【核心】手动构建一个完美格式的 location 消息对象
        aiMessage = {
            ...baseMessage, // 复用已有的发送者、时间戳等信息
            type: 'location',
            userLocation: locationData.userLocation || '',
            aiLocation: locationData.aiLocation || '',
            distance: locationData.distance,
            trajectoryPoints: locationData.trajectoryPoints || []
        };
        
        // 6. 将这个完美的对象推入待处理列表，并跳过后续的 switch-case
        // (因为我们已经处理完这条消息了)
        chat.history.push(aiMessage);
        if (isViewingThisChat) {
             appendMessage(aiMessage, chat);
        }
        console.log("定位指令解析成功并已创建消息对象:", aiMessage);
        
        // 使用 continue 跳过本次循环的剩余部分，直接处理下一条AI回复
        continue; 
    }
}
// ▲▲▲ 粘贴到这里结束 ▲▲▲

            switch (msgData.type) {
                case 'waimai_response':
                    const requestMessageIndex = chat.history.findIndex(m => m.timestamp === msgData.for_timestamp);
                    if (requestMessageIndex > -1) {
                        const originalMsg = chat.history[requestMessageIndex];
                        originalMsg.status = msgData.status;
                        originalMsg.paidBy = msgData.status === 'paid' ? msgData.name : null;
                    }
                    continue;

case 'qzone_post':
    const newPost = { 
        type: msgData.postType, 
        content: msgData.content || '', 
        publicText: msgData.publicText || '', 
        hiddenContent: msgData.hiddenContent || '', 
        timestamp: Date.now(), 
        authorId: chatId, 
        authorGroupId: chat.groupId, // 【核心新增】记录作者的分组ID
        visibleGroupIds: null 
    };
    await db.qzonePosts.add(newPost);
                    updateUnreadIndicator(unreadPostsCount + 1);
                    if (isViewingThisChat && document.getElementById('qzone-screen').classList.contains('active')) {
                       await renderQzonePosts();
                    }
                    continue;

                case 'qzone_comment':
                    const postToComment = await db.qzonePosts.get(parseInt(msgData.postId));
                    if (postToComment) {
                        if (!postToComment.comments) postToComment.comments = [];
                        postToComment.comments.push({ commenterName: chat.name, text: msgData.commentText, timestamp: Date.now() });
                        await db.qzonePosts.update(postToComment.id, { comments: postToComment.comments });
                        updateUnreadIndicator(unreadPostsCount + 1);
                        if (isViewingThisChat && document.getElementById('qzone-screen').classList.contains('active')) {
                           await renderQzonePosts();
                        }
                    }
                    continue;

                case 'qzone_like':
                   const postToLike = await db.qzonePosts.get(parseInt(msgData.postId));
                   if (postToLike) {
                       if (!postToLike.likes) postToLike.likes = [];
                       if (!postToLike.likes.includes(chat.name)) {
                           postToLike.likes.push(chat.name);
                           await db.qzonePosts.update(postToLike.id, { likes: postToLike.likes });
                           updateUnreadIndicator(unreadPostsCount + 1);
                           if (isViewingThisChat && document.getElementById('qzone-screen').classList.contains('active')) {
                              await renderQzonePosts();
                           }
                       }
                   }
                    continue;

                case 'video_call_request':
                    if (!videoCallState.isActive && !videoCallState.isAwaitingResponse) {
                        state.activeChatId = chatId;
                        videoCallState.activeChatId = chatId; 
                        videoCallState.isAwaitingResponse = true;
                        videoCallState.isGroupCall = chat.isGroup;
                        videoCallState.callRequester = msgData.name || chat.name;
                        showIncomingCallModal(chatId); // <--- 把chatId作为参数传进去
    }
                    continue;

            case 'group_call_request':
                if (!videoCallState.isActive && !videoCallState.isAwaitingResponse) {
                    state.activeChatId = chatId;
                    videoCallState.isAwaitingResponse = true;
                    videoCallState.isGroupCall = true;
                    videoCallState.initiator = 'ai';
                    videoCallState.callRequester = msgData.name;
                    showIncomingCallModal();
                }
                continue;

                case 'pat_user':
                    const suffix = msgData.suffix ? ` ${msgData.suffix.trim()}` : '';
                    const patText = `${msgData.name || chat.name} 拍了拍我${suffix}`;
                    const patMessage = { 
                        role: 'system', 
                        type: 'pat_message', 
                        content: patText, 
                        timestamp: Date.now() 
                    };
                    chat.history.push(patMessage);
                    if (isViewingThisChat) {
                        const phoneScreen = document.getElementById('phone-screen');
                        phoneScreen.classList.remove('pat-animation');
                        void phoneScreen.offsetWidth;
                        phoneScreen.classList.add('pat-animation');
                        setTimeout(() => phoneScreen.classList.remove('pat-animation'), 500);
                        appendMessage(patMessage, chat);
                    } else {
                        showNotification(chatId, patText);
                    }
                    continue; 

                case 'update_status':
                    chat.status.text = msgData.status_text;
                    chat.status.isBusy = msgData.is_busy || false;
                    chat.status.lastUpdate = Date.now();
                    
                    const statusUpdateMessage = {
                        role: 'system',
                        type: 'pat_message',
                        content: `[${chat.name}的状态已更新为: ${msgData.status_text}]`,
                        timestamp: Date.now()
                    };
                    chat.history.push(statusUpdateMessage);

                    if (isViewingThisChat) {
                        appendMessage(statusUpdateMessage, chat);
                    }
                    
                    renderChatList(); 
                    
                    continue; 

                case 'change_music':
                    if (musicState.isActive && musicState.activeChatId === chatId) {
                        const songNameToFind = msgData.song_name;
                        
                        const targetSongIndex = musicState.playlist.findIndex(
                            track => track.name.toLowerCase() === songNameToFind.toLowerCase()
                        );

                        if (targetSongIndex > -1) {
                            playSong(targetSongIndex);

                            const track = musicState.playlist[targetSongIndex];
                            const musicChangeMessage = {
                                role: 'system',
                                type: 'pat_message',
                                content: `[♪ ${chat.name} 为你切歌: 《${track.name}》 - ${track.artist}]`,
                                timestamp: Date.now()
                            };
                            chat.history.push(musicChangeMessage);

                            if (isViewingThisChat) {
                                appendMessage(musicChangeMessage, chat);
                            }
                        }
                    }
                    continue;
                case 'create_memory':
                    const newMemory = {
                        chatId: chatId,
                        authorName: chat.name,
                        description: msgData.description,
                        timestamp: Date.now(),
                        type: 'ai_generated'
                    };
                    await db.memories.add(newMemory);

                    console.log(`AI "${chat.name}" 记录了一条新回忆:`, msgData.description);
                    
                    continue; 

        case 'create_countdown':
            const targetDate = new Date(msgData.date);
            if (!isNaN(targetDate) && targetDate > new Date()) {
                const newCountdown = {
                    chatId: chatId,
                    authorName: chat.name,
                    description: msgData.title,
                    timestamp: Date.now(),
                    type: 'countdown',
                    targetDate: targetDate.getTime()
                };
                await db.memories.add(newCountdown);
                console.log(`AI "${chat.name}" 创建了一个新约定:`, msgData.title);
            }
            continue;

    case 'block_user':
        if (!chat.isGroup) {
            chat.relationship.status = 'blocked_by_ai';

        // ▼▼▼ 在这里添加下面的代码 ▼▼▼
        const hiddenMessage = {
            role: 'system',
            content: `[系统提示：你刚刚主动拉黑了用户。]`,
            timestamp: Date.now(),
            isHidden: true
        };
        chat.history.push(hiddenMessage);
        // ▲▲▲ 添加结束 ▲▲▲

            await db.chats.put(chat);
            
            if (isViewingThisChat) {
                renderChatInterface(chatId);
            }
            renderChatList();
            
            break; 
        }
        continue;
                case 'friend_request_response':
                    if (!chat.isGroup && chat.relationship.status === 'pending_ai_approval') {
                        if (msgData.decision === 'accept') {
                            chat.relationship.status = 'friend';
                            aiMessage = { ...baseMessage, content: "我通过了你的好友申请，我们现在是好友啦！" };
                        } else {
                            chat.relationship.status = 'blocked_by_ai';
                            aiMessage = { ...baseMessage, content: "抱歉，我拒绝了你的好友申请。" };
                        }
                        chat.relationship.applicationReason = '';
                    }
                    break;
                case 'poll':
                    const pollOptions = typeof msgData.options === 'string'
                        ? msgData.options.split('\n').filter(opt => opt.trim())
                        : (Array.isArray(msgData.options) ? msgData.options : []);
                    
                    if (pollOptions.length < 2) continue;

                    aiMessage = {
                        ...baseMessage,
                        type: 'poll',
                        question: msgData.question,
                        options: pollOptions,
                        votes: {},
                        isClosed: false,
                    };
                    break;
                
                case 'vote':
                    const pollToVote = chat.history.find(m => m.timestamp === msgData.poll_timestamp);
                    if (pollToVote && !pollToVote.isClosed) {
                        Object.keys(pollToVote.votes).forEach(option => {
                            const voterIndex = pollToVote.votes[option].indexOf(msgData.name);
                            if (voterIndex > -1) {
                                pollToVote.votes[option].splice(voterIndex, 1);
                            }
                        });
                        if (!pollToVote.votes[msgData.choice]) {
                            pollToVote.votes[msgData.choice] = [];
                        }

// ▼▼▼ 在这里添加新代码 ▼▼▼
const member = chat.members.find(m => m.originalName === msgData.name);
const displayName = member ? member.groupNickname : msgData.name;
// ▲▲▲ 添加结束 ▲▲▲

if (!pollToVote.votes[msgData.choice].includes(displayName)) { // 【核心修改】
    pollToVote.votes[msgData.choice].push(displayName); // 【核心修改】
}                     
                        
                        if (isViewingThisChat) {
                            renderChatInterface(chatId);
                        }
                    }
                    continue;

    case 'red_packet':
        aiMessage = {
            ...baseMessage,
            type: 'red_packet',
            packetType: msgData.packetType,
            totalAmount: msgData.amount,
            count: msgData.count,
            greeting: msgData.greeting,
            receiverName: msgData.receiver,
            claimedBy: {},
            isFullyClaimed: false,
        };
        break;
case 'open_red_packet':
    const packetToOpen = chat.history.find(m => m.timestamp === msgData.packet_timestamp);
    if (packetToOpen && !packetToOpen.isFullyClaimed && !(packetToOpen.claimedBy && packetToOpen.claimedBy[msgData.name])) {

        // 1. 根据AI的本名(msgData.name)去成员列表里找到完整的成员对象
        const member = chat.members.find(m => m.originalName === msgData.name);
        // 2. 获取该成员当前的群昵称，如果找不到（异常情况），则备用其本名
        const displayName = member ? member.groupNickname : msgData.name;
        
        let claimedAmountAI = 0;
        const remainingAmount = packetToOpen.totalAmount - Object.values(packetToOpen.claimedBy || {}).reduce((sum, val) => sum + val, 0);
        const remainingCount = packetToOpen.count - Object.keys(packetToOpen.claimedBy || {}).length;

        if (remainingCount > 0) {
            if (remainingCount === 1) { claimedAmountAI = remainingAmount; } 
            else {
                const min = 0.01;
                const max = remainingAmount - (remainingCount - 1) * min;
                claimedAmountAI = Math.random() * (max - min) + min;
            }
            claimedAmountAI = parseFloat(claimedAmountAI.toFixed(2));
            
            if (!packetToOpen.claimedBy) packetToOpen.claimedBy = {};
            // 【核心修改】使用我们刚刚查找到的 displayName 作为记录的key
            packetToOpen.claimedBy[displayName] = claimedAmountAI;
            
            const aiClaimedMessage = {
                role: 'system',
                type: 'pat_message',
                // 【核心修改】系统消息里也使用 displayName
                content: `${displayName} 领取了 ${packetToOpen.senderName} 的红包`,
                timestamp: Date.now()
            };
            chat.history.push(aiClaimedMessage);

            let hiddenContentForAI = `[系统提示：你 (${displayName}) 成功抢到了 ${claimedAmountAI.toFixed(2)} 元。`; // 【核心修改】

            if (Object.keys(packetToOpen.claimedBy).length >= packetToOpen.count) {
                packetToOpen.isFullyClaimed = true;
                
                const finishedMessage = {
                    role: 'system',
                    type: 'pat_message',
                    content: `${packetToOpen.senderName} 的红包已被领完`,
                    timestamp: Date.now() + 1
                };
                chat.history.push(finishedMessage);
                
                let luckyKing = { name: '', amount: -1 };
                if (packetToOpen.packetType === 'lucky' && packetToOpen.count > 1) {
                    Object.entries(packetToOpen.claimedBy).forEach(([name, amount]) => {
                        if (amount > luckyKing.amount) {
                            luckyKing = { name, amount };
                        }
                    });
                }
                if (luckyKing.name) {
                     hiddenContentForAI += ` 红包已被领完，手气王是 ${luckyKing.name}！`;
                } else {
                     hiddenContentForAI += ` 红包已被领完。`;
                }
            }
            hiddenContentForAI += ' 请根据这个结果发表你的评论。]';

            const hiddenMessageForAI = {
                role: 'system',
                content: hiddenContentForAI,
                timestamp: Date.now() + 2,
                isHidden: true
            };
            chat.history.push(hiddenMessageForAI);
        }
        
        if (isViewingThisChat) {
            renderChatInterface(chatId);
        }
    }
    continue;
case 'change_avatar':
    const avatarName = msgData.name;
    // 在该角色的头像库中查找
    const foundAvatar = chat.settings.aiAvatarLibrary.find(avatar => avatar.name === avatarName);
    
    if (foundAvatar) {
        // 找到了，就更新头像
        chat.settings.aiAvatar = foundAvatar.url;
        
        // 创建一条系统提示，告知用户头像已更换
        const systemNotice = {
            role: 'system',
            type: 'pat_message', // 复用居中样式
            content: `[${chat.name} 更换了头像]`,
            timestamp: Date.now()
        };
        chat.history.push(systemNotice);
        
        // 如果在当前聊天界面，则实时渲染
        if (isViewingThisChat) {
            appendMessage(systemNotice, chat);
            // 立刻刷新聊天界面以显示新头像
            renderChatInterface(chatId);
        }
    }
    // 处理完后，继续处理AI可能返回的其他消息
    continue;

// ▼▼▼ 在 triggerAiResponse 的 switch 语句中，【添加】这两个全新的 case ▼▼▼

                case 'accept_transfer': { // 使用大括号创建块级作用域
                    const originalTransferMsgIndex = chat.history.findIndex(m => m.timestamp === msgData.for_timestamp);
                    if (originalTransferMsgIndex > -1) {
                        const originalMsg = chat.history[originalTransferMsgIndex];
                        originalMsg.status = 'accepted';
                    }
                    continue; // 接受指令只修改状态，不产生新消息
                }

                case 'decline_transfer': { // 使用大括号创建块级作用域
                    const originalTransferMsgIndex = chat.history.findIndex(m => m.timestamp === msgData.for_timestamp);
                    if (originalTransferMsgIndex > -1) {
                        const originalMsg = chat.history[originalTransferMsgIndex];
                        originalMsg.status = 'declined';
                        
                        // 【核心】创建一条新的“退款”消息
                        const refundMessage = {
                            role: 'assistant',
                            senderName: chat.name,
                            type: 'transfer',
                            isRefund: true, // 标记这是一条退款消息
                            amount: originalMsg.amount,
                            note: '转账已被拒收',
                            timestamp: messageTimestamp++ // 使用递增的时间戳
                        };
                        
                        // 将新消息推入历史记录，它会被后续的循环处理并渲染
                        chat.history.push(refundMessage);

        // ▼▼▼ 在这里添加下面的代码 ▼▼▼
        if (isViewingThisChat) {
            // 因为退款消息是新生成的，所以我们直接将它添加到界面上
            appendMessage(refundMessage, chat); 
            // 同时，原始的转账消息状态变了，所以要重绘整个界面以更新它
            renderChatInterface(chatId); 
        }
        // ▲▲▲ 添加结束 ▲▲▲

                    }
                    continue; // 继续处理AI返回的文本消息
                }

// ▲▲▲ 添加结束 ▲▲▲

    case 'system_message':
        aiMessage = { role: 'system', type: 'pat_message', content: msgData.content, timestamp: Date.now() };
        break;

// ▼▼▼ 在 triggerAiResponse 的 switch 语句中，【必须添加】这个新的 case ▼▼▼

                case 'share_link':
                    aiMessage = { 
                        ...baseMessage, 
                        type: 'share_link',
                        title: msgData.title,
                        description: msgData.description,
                        // thumbnail_url: msgData.thumbnail_url, // 我们已经决定不要图片了，所以这行可以不要
                        source_name: msgData.source_name,
                        content: msgData.content // 这是文章正文，点击卡片后显示的内容
                    };
                    break;

// ▲▲▲ 添加结束 ▲▲▲

// ▼▼▼ 在 triggerAiResponse 的 switch (msgData.type) 语句中，添加这个新的 case ▼▼▼
case 'quote_reply':
    const originalMessage = chat.history.find(m => m.timestamp === msgData.target_timestamp);
    if (originalMessage) {
        const quoteContext = {
            timestamp: originalMessage.timestamp,
            senderName: originalMessage.senderName || (originalMessage.role === 'user' ? (chat.settings.myNickname || '我') : chat.name),
            content: String(originalMessage.content || '').substring(0, 50),
        };
        aiMessage = { 
            ...baseMessage, 
            content: msgData.reply_content,
            quote: quoteContext // 核心：在这里附加引用对象
        };
    } else {
        // 如果找不到被引用的消息，就当作普通消息发送
        aiMessage = { ...baseMessage, content: msgData.reply_content };
    }
    break;
// ▲▲▲ 新增 case 结束 ▲▲▲

case 'location':
    aiMessage = {
        ...baseMessage,
        type: 'location',
        userLocation: msgData.userLocation,
        aiLocation: msgData.aiLocation,
        distance: msgData.distance,
        trajectoryPoints: msgData.trajectoryPoints || [] // 【新增】确保即使AI没提供，也是一个空数组
    };
    break;
// ▲▲▲ 替换结束 ▲▲▲

case 'send_and_recall': {
    // --- 动画部分 (保持不变) ---
    if (!isViewingThisChat) continue;
    const tempMessageData = { ...baseMessage, content: msgData.content };
    appendMessage(tempMessageData, chat, true);
    await new Promise(resolve => setTimeout(resolve, Math.random() * 1000 + 1500));
    const bubbleWrapper = document.querySelector(`.message-bubble[data-timestamp="${tempMessageData.timestamp}"]`)?.closest('.message-wrapper');
    if (bubbleWrapper) {
        bubbleWrapper.classList.add('recalled-animation');
        await new Promise(resolve => setTimeout(resolve, 300));
    }

    // --- 核心修改：数据记录与AI感知 ---
    
    // 1. 创建对用户可见的“已撤回”消息
    const recalledMessage = {
        role: 'assistant',
        senderName: msgData.name || chat.name,
        type: 'recalled_message',
        content: '对方撤回了一条消息',
        timestamp: tempMessageData.timestamp,
        recalledData: { originalType: 'text', originalContent: msgData.content }
    };
    
    // 2. 【关键】创建一条对用户隐藏、但对AI可见的“记忆”消息
    const hiddenMemoryMessage = {
        role: 'system', // 必须是 system，这样AI才知道这是上下文信息
        content: `[系统提示：你刚刚说了一句“${msgData.content}”，但立刻就撤回了它。]`,
        timestamp: tempMessageData.timestamp + 1, // 确保在撤回消息之后
        isHidden: true // 这个标记让它不在UI上显示
    };

    // 3. 将这两条消息都添加到历史记录中
    chat.history.push(recalledMessage, hiddenMemoryMessage);
    
    // 4. 替换DOM，显示“已撤回”提示
    const placeholder = createMessageElement(recalledMessage, chat);
    if(document.body.contains(bubbleWrapper)) {
        bubbleWrapper.parentNode.replaceChild(placeholder, bubbleWrapper);
    }
    
    continue;
}
                
                case 'text':
                    aiMessage = { ...baseMessage, content: String(msgData.content || msgData.message) };
                    break;
                case 'sticker':
                    aiMessage = { ...baseMessage, type: 'sticker', content: msgData.url, meaning: msgData.meaning || '' };
                    break;
                case 'ai_image':
                    aiMessage = { ...baseMessage, type: 'ai_image', content: msgData.description };
                    break;
                case 'voice_message':
                    aiMessage = { ...baseMessage, type: 'voice_message', content: msgData.content };
                    break;
                case 'transfer':
                    aiMessage = { ...baseMessage, type: 'transfer', amount: msgData.amount, note: msgData.note, receiverName: msgData.receiver || '我' };
                    break;
                
                case 'waimai_request':
                    aiMessage = { 
                        ...baseMessage, 
                        type: 'waimai_request',
                        productInfo: msgData.productInfo,
                        amount: msgData.amount,
                        status: 'pending',
                        countdownEndTime: Date.now() + 15 * 60 * 1000,
                    };
                    break;
                
                default:
                     console.warn("收到了未知的AI指令类型:", msgData.type);
                     break;
            }

            // 【核心修复】将渲染逻辑移出循环
            if (aiMessage) {
                // 1. 将新消息存入历史记录
                chat.history.push(aiMessage);

                if (!isViewingThisChat && !notificationShown) {
                    let notificationText;
                    switch (aiMessage.type) {
                        case 'transfer':
                            notificationText = `[收到一笔转账]`;
                            break;
                        case 'waimai_request':
                            notificationText = `[收到一个外卖代付请求]`;
                            break;
                        case 'ai_image':
                            notificationText = `[图片]`;
                            break;
                        case 'voice_message':
                            notificationText = `[语音]`;
                            break;
                        case 'sticker':
                            notificationText = aiMessage.meaning ? `[表情: ${aiMessage.meaning}]` : '[表情]';
                            break;
                        default:
                            notificationText = String(aiMessage.content || '');
                    }
                    const finalNotifText = chat.isGroup ? `${aiMessage.senderName}: ${notificationText}` : notificationText;
                    showNotification(chatId, finalNotifText.substring(0, 40) + (finalNotifText.length > 40 ? '...' : ''));
                    notificationShown = true; // 确保只通知一次
                }

    if (!isViewingThisChat) {
        // 如果用户不在当前聊天界面，就把这个聊天的未读数 +1
        chat.unreadCount = (chat.unreadCount || 0) + 1;
    }
                
                // 2. 只有在当前聊天界面时，才执行带动画的添加
                if (isViewingThisChat) {
                    appendMessage(aiMessage, chat);
                    // 3. 【关键】在这里暂停一小会儿，给动画播放的时间
                    await new Promise(resolve => setTimeout(resolve, Math.random() * 1800 + 1000));
                }
            }
  }        

        if (callHasBeenHandled && videoCallState.isGroupCall) {
            videoCallState.isAwaitingResponse = false;
            if (videoCallState.participants.length > 0) {
                startVideoCall();
            } else {
                videoCallState = { ...videoCallState, isAwaitingResponse: false, participants: [] };
                showScreen('chat-interface-screen');
                alert('无人接听群聊邀请。');
            }
        }
        
        await db.chats.put(chat);

    } catch (error) {
        chat.history = chat.history.filter(msg => !msg.isTemporary);
        if (!chat.isGroup && chat.relationship?.status === 'pending_ai_approval') {
            chat.relationship.status = 'blocked_by_ai';
            await showCustomAlert('申请失败', `AI在处理你的好友申请时出错了，请稍后重试。\n错误信息: ${error.message}`);
        } else {
            const errorContent = `[出错了: ${error.message}]`;
            const errorMessage = { role: 'assistant', content: errorContent, timestamp: Date.now() };
            if(chat.isGroup) errorMessage.senderName = "系统消息";
            chat.history.push(errorMessage);
        }
        
        await db.chats.put(chat);        
        videoCallState.isAwaitingResponse = false;

        if(document.getElementById('chat-interface-screen').classList.contains('active') && state.activeChatId === chatId) {
            renderChatInterface(chatId);
        }
    } finally {
        // ★★★★★【核心修改4：在 finally 块中统一隐藏所有类型的提示】★★★★★
        if (chat.isGroup) {
            if (typingIndicator) {
                typingIndicator.style.display = 'none';
            }
        } else {
            if (chatHeaderTitle && state.chats[chatId]) {
                chatHeaderTitle.style.opacity = 0;
                setTimeout(() => {
                    chatHeaderTitle.textContent = state.chats[chatId].name;
                    chatHeaderTitle.classList.remove('typing-status');
                    chatHeaderTitle.style.opacity = 1;
                }, 200);
            }
        }
        renderChatList();
    }
}

        async function sendSticker(sticker) { if (!state.activeChatId) return; const chat = state.chats[state.activeChatId]; const msg = { role: 'user', content: sticker.url, meaning: sticker.name, timestamp: Date.now() }; chat.history.push(msg); await db.chats.put(chat); appendMessage(msg, chat); renderChatList(); document.getElementById('sticker-panel').classList.remove('visible'); }

        async function sendUserTransfer() { if (!state.activeChatId) return; const amountInput = document.getElementById('transfer-amount'); const noteInput = document.getElementById('transfer-note'); const amount = parseFloat(amountInput.value); const note = noteInput.value.trim(); if (isNaN(amount) || amount < 0 || amount > 9999999999999) { alert('请输入有效的金额 (0 到 9999999999999 之间)！'); return; } const chat = state.chats[state.activeChatId]; const senderName = chat.isGroup ? (chat.settings.myNickname || '我') : '我'; const receiverName = chat.isGroup ? '群聊' : chat.name; const msg = { role: 'user', type: 'transfer', amount: amount, note: note, senderName, receiverName, timestamp: Date.now() }; chat.history.push(msg); await db.chats.put(chat); appendMessage(msg, chat); renderChatList(); document.getElementById('transfer-modal').classList.remove('visible'); amountInput.value = ''; noteInput.value = ''; }

        function enterSelectionMode(initialMsgTimestamp) { if (isSelectionMode) return; isSelectionMode = true; document.getElementById('chat-interface-screen').classList.add('selection-mode'); toggleMessageSelection(initialMsgTimestamp); }

        function exitSelectionMode() {
    cleanupWaimaiTimers(); // <--- 在这里添加这行代码
 if (!isSelectionMode) return; isSelectionMode = false; document.getElementById('chat-interface-screen').classList.remove('selection-mode'); selectedMessages.forEach(ts => { const bubble = document.querySelector(`.message-bubble[data-timestamp="${ts}"]`); if (bubble) bubble.classList.remove('selected'); }); selectedMessages.clear(); }

// ▼▼▼ 请用这个【最终简化版】替换旧的 toggleMessageSelection 函数 ▼▼▼
function toggleMessageSelection(timestamp) {
    // 【核心修正】选择器已简化，不再寻找已删除的 .recalled-message-placeholder
    const elementToSelect = document.querySelector(
        `.message-bubble[data-timestamp="${timestamp}"]`
    );

    if (!elementToSelect) return;

    if (selectedMessages.has(timestamp)) {
        selectedMessages.delete(timestamp);
        elementToSelect.classList.remove('selected');
    } else {
        selectedMessages.add(timestamp);
        elementToSelect.classList.add('selected');
    }
    
    document.getElementById('selection-count').textContent = `已选 ${selectedMessages.size} 条`;
    
    if (selectedMessages.size === 0) {
        exitSelectionMode();
    }
}
// ▲▲▲ 替换结束 ▲▲▲

        function addLongPressListener(element, callback) { let pressTimer; const startPress = (e) => { if(isSelectionMode) return; e.preventDefault(); pressTimer = window.setTimeout(() => callback(e), 500); }; const cancelPress = () => clearTimeout(pressTimer); element.addEventListener('mousedown', startPress); element.addEventListener('mouseup', cancelPress); element.addEventListener('mouseleave', cancelPress); element.addEventListener('touchstart', startPress, { passive: true }); element.addEventListener('touchend', cancelPress); element.addEventListener('touchmove', cancelPress); }

        async function handleListenTogetherClick() { 

    // ▼▼▼ 在这里添加下面这行代码 ▼▼▼
    document.getElementById('floating-lyrics-bar').style.display = 'none';
    // ▲▲▲ 添加结束 ▲▲▲

const targetChatId = state.activeChatId; if (!targetChatId) return; if (!musicState.isActive) { startListenTogetherSession(targetChatId); return; } if (musicState.activeChatId === targetChatId) { document.getElementById('music-player-overlay').classList.add('visible'); } else { const oldChatName = state.chats[musicState.activeChatId]?.name || '未知'; const newChatName = state.chats[targetChatId]?.name || '当前'; const confirmed = await showCustomConfirm('切换听歌对象', `您正和「${oldChatName}」听歌。要结束并开始和「${newChatName}」的新会话吗？`, { confirmButtonClass: 'btn-danger' }); if (confirmed) { await endListenTogetherSession(true); await new Promise(resolve => setTimeout(resolve, 50)); startListenTogetherSession(targetChatId); } } }

        async function startListenTogetherSession(chatId) { const chat = state.chats[chatId]; if (!chat) return; musicState.totalElapsedTime = chat.musicData.totalTime || 0; musicState.isActive = true; musicState.activeChatId = chatId; if (musicState.playlist.length > 0) { musicState.currentIndex = 0; } else { musicState.currentIndex = -1; } if(musicState.timerId) clearInterval(musicState.timerId); musicState.timerId = setInterval(() => { if (musicState.isPlaying) { musicState.totalElapsedTime++; updateElapsedTimeDisplay(); } }, 1000); updatePlayerUI(); updatePlaylistUI(); document.getElementById('music-player-overlay').classList.add('visible'); }

async function endListenTogetherSession(saveState = true) {
    if (!musicState.isActive) return;
    const oldChatId = musicState.activeChatId;
    const cleanupLogic = async () => {

    // ▼▼▼ 在这里添加下面这行代码 ▼▼▼
    document.getElementById('floating-lyrics-bar').style.display = 'none';
    // ▲▲▲ 添加结束 ▲▲▲

        if (musicState.timerId) clearInterval(musicState.timerId);
        if (musicState.isPlaying) audioPlayer.pause();
        if (saveState && oldChatId && state.chats[oldChatId]) {
            const chat = state.chats[oldChatId];
            chat.musicData.totalTime = musicState.totalElapsedTime;
            await db.chats.put(chat);
        }
        musicState.isActive = false;
        musicState.activeChatId = null;
        musicState.totalElapsedTime = 0;
        musicState.timerId = null;
        updateListenTogetherIcon(oldChatId, true);
    };
    closeMusicPlayerWithAnimation(cleanupLogic);
}

function returnToChat() {
    closeMusicPlayerWithAnimation(() => {

        if (musicState.isActive && lyricsBarSettings.showOnClose) {
            document.getElementById('floating-lyrics-bar').style.display = 'flex';
        }
    });
}

        function updateListenTogetherIcon(chatId, forceReset = false) { const iconImg = document.querySelector('#listen-together-btn img'); if(!iconImg) return; if(forceReset || !musicState.isActive || musicState.activeChatId !== chatId) { iconImg.src = 'https://i.postimg.cc/8kYShvrJ/90-UI-2.png'; iconImg.className = ''; return; } iconImg.src = 'https://i.postimg.cc/D0pq6qS2/E30078-DC-8-B99-4-C01-AFDA-74728-DBF7-BEA.png'; iconImg.classList.add('rotating'); if (musicState.isPlaying) iconImg.classList.remove('paused'); else iconImg.classList.add('paused'); }
        window.updateListenTogetherIconProxy = updateListenTogetherIcon;

        function updatePlayerUI() { updateListenTogetherIcon(musicState.activeChatId); updateElapsedTimeDisplay(); const titleEl = document.getElementById('music-player-song-title'); const artistEl = document.getElementById('music-player-artist'); const playPauseBtn = document.getElementById('music-play-pause-btn'); if (musicState.currentIndex > -1 && musicState.playlist.length > 0) { const track = musicState.playlist[musicState.currentIndex]; titleEl.textContent = track.name; artistEl.textContent = track.artist; } else { titleEl.textContent = '请添加歌曲'; artistEl.textContent = '...'; } playPauseBtn.textContent = musicState.isPlaying ? '❚❚' : '▶'; }

        function updateElapsedTimeDisplay() { const hours = (musicState.totalElapsedTime / 3600).toFixed(1); document.getElementById('music-time-counter').textContent = `已经一起听了${hours}小时`; }

function updatePlaylistUI() {
    const playlistBody = document.getElementById('playlist-body');
    playlistBody.innerHTML = '';
    if (musicState.playlist.length === 0) {
        playlistBody.innerHTML = '<p style="text-align:center; padding: 20px; color: #888;">播放列表是空的~</p>';
        return;
    }
    musicState.playlist.forEach((track, index) => {
        const item = document.createElement('div');
        item.className = 'playlist-item';
        if(index === musicState.currentIndex) item.classList.add('playing');
        item.innerHTML = `
            <div class="playlist-item-info">
                <div class="title">${track.name}</div>
                <div class="artist">${track.artist}</div>
            </div>
            <div class="playlist-item-actions">
                <span class="playlist-action-btn lyrics-btn" data-index="${index}">词</span>
                <span class="playlist-action-btn delete-track-btn" data-index="${index}">×</span>
            </div>
        `;
        item.querySelector('.playlist-item-info').addEventListener('click', () => playSong(index));
        playlistBody.appendChild(item);
    });
}

function playSong(index) {
    if (index < 0 || index >= musicState.playlist.length) return;
    musicState.currentIndex = index;
    const track = musicState.playlist[index];
    musicState.parsedLyrics = parseLRC(track.lrcContent || "");
    musicState.currentLyricIndex = -1;
    renderLyrics();
    if (track.isLocal && track.src instanceof Blob) {
        audioPlayer.src = URL.createObjectURL(track.src);
    } else if (!track.isLocal) {
        audioPlayer.src = track.src;
    } else {
        console.error('本地歌曲源错误:', track);
        return;
    }
    audioPlayer.play();
    updatePlaylistUI();
    updatePlayerUI();
    updateMusicProgressBar();
}

        function togglePlayPause() { if (audioPlayer.paused) { if (musicState.currentIndex === -1 && musicState.playlist.length > 0) { playSong(0); } else if (musicState.currentIndex > -1) { audioPlayer.play(); } } else { audioPlayer.pause(); } }

        function playNext() { if (musicState.playlist.length === 0) return; let nextIndex; switch(musicState.playMode) { case 'random': nextIndex = Math.floor(Math.random() * musicState.playlist.length); break; case 'single': playSong(musicState.currentIndex); return; case 'order': default: nextIndex = (musicState.currentIndex + 1) % musicState.playlist.length; break; } playSong(nextIndex); }

        function playPrev() { if (musicState.playlist.length === 0) return; const newIndex = (musicState.currentIndex - 1 + musicState.playlist.length) % musicState.playlist.length; playSong(newIndex); }

        function changePlayMode() { const modes = ['order', 'random', 'single']; const currentModeIndex = modes.indexOf(musicState.playMode); musicState.playMode = modes[(currentModeIndex + 1) % modes.length]; document.getElementById('music-mode-btn').textContent = {'order': '顺序', 'random': '随机', 'single': '单曲'}[musicState.playMode]; }

        async function addSongFromURL() { const url = await showCustomPrompt("添加网络歌曲", "请输入歌曲的URL", "", "url"); if (!url) return; const name = await showCustomPrompt("歌曲信息", "请输入歌名"); if (!name) return; const artist = await showCustomPrompt("歌曲信息", "请输入歌手名"); if (!artist) return; musicState.playlist.push({ name, artist, src: url, isLocal: false }); await saveGlobalPlaylist(); updatePlaylistUI(); if(musicState.currentIndex === -1) { musicState.currentIndex = musicState.playlist.length - 1; updatePlayerUI(); } }

async function addSongFromLocal(event) {
    const files = event.target.files;
    if (!files.length) return;

    for (const file of files) {
        let name = file.name.replace(/\.[^/.]+$/, "");
        name = await showCustomPrompt("歌曲信息", "请输入歌名", name);
        if (name === null) continue;
        
        const artist = await showCustomPrompt("歌曲信息", "请输入歌手名", "未知歌手");
        if (artist === null) continue;

        let lrcContent = "";
        const wantLrc = await showCustomConfirm("导入歌词", `要为《${name}》导入歌词文件 (.lrc) 吗？`);
        if (wantLrc) {
            lrcContent = await new Promise(resolve => {
                const lrcInput = document.getElementById('lrc-upload-input');
                const lrcChangeHandler = (e) => {
                    const lrcFile = e.target.files[0];
                    if (lrcFile) {
                        const reader = new FileReader();
                        reader.onload = (readEvent) => resolve(readEvent.target.result);
                        reader.onerror = () => resolve("");
                        reader.readAsText(lrcFile);
                    } else {
                        resolve("");
                    }
                    lrcInput.removeEventListener('change', lrcChangeHandler);
                    lrcInput.value = '';
                };
                lrcInput.addEventListener('change', lrcChangeHandler);
                lrcInput.click();
            });
        }
        
        musicState.playlist.push({ 
            name, 
            artist, 
            src: file, 
            isLocal: true,
            lrcContent: lrcContent
        });
    }
    
    await saveGlobalPlaylist();
    updatePlaylistUI();
    if (musicState.currentIndex === -1 && musicState.playlist.length > 0) {
        musicState.currentIndex = 0;
        updatePlayerUI();
    }
    event.target.value = null;
}

        async function deleteTrack(index) { if (index < 0 || index >= musicState.playlist.length) return; const track = musicState.playlist[index]; const wasPlaying = musicState.isPlaying && musicState.currentIndex === index; if (track.isLocal && audioPlayer.src.startsWith('blob:') && musicState.currentIndex === index) URL.revokeObjectURL(audioPlayer.src); musicState.playlist.splice(index, 1); await saveGlobalPlaylist(); if (musicState.playlist.length === 0) { if (musicState.isPlaying) audioPlayer.pause(); audioPlayer.src = ''; musicState.currentIndex = -1; musicState.isPlaying = false; } else { if (wasPlaying) { playNext(); } else { if (musicState.currentIndex >= index) musicState.currentIndex = Math.max(0, musicState.currentIndex - 1); } } updatePlayerUI(); updatePlaylistUI(); }

        const personaLibraryModal = document.getElementById('persona-library-modal');
        const personaEditorModal = document.getElementById('persona-editor-modal');
        const presetActionsModal = document.getElementById('preset-actions-modal');

        function openPersonaLibrary() { renderPersonaLibrary(); personaLibraryModal.classList.add('visible'); }

        function closePersonaLibrary() { personaLibraryModal.classList.remove('visible'); }

        function renderPersonaLibrary() { const grid = document.getElementById('persona-library-grid'); grid.innerHTML = ''; if (state.personaPresets.length === 0) { grid.innerHTML = '<p style="color: var(--text-secondary); grid-column: 1 / -1; text-align: center; margin-top: 20px;">空空如也~ 点击右上角"添加"来创建你的第一个人设预设吧！</p>'; return; } state.personaPresets.forEach(preset => { const item = document.createElement('div'); item.className = 'persona-preset-item'; item.style.backgroundImage = `url(${preset.avatar})`; item.dataset.presetId = preset.id; item.addEventListener('click', () => applyPersonaPreset(preset.id)); addLongPressListener(item, () => showPresetActions(preset.id)); grid.appendChild(item); }); }

        function showPresetActions(presetId) { editingPersonaPresetId = presetId; presetActionsModal.classList.add('visible'); }

        function hidePresetActions() { presetActionsModal.classList.remove('visible'); editingPersonaPresetId = null; }

        function applyPersonaPreset(presetId) { const preset = state.personaPresets.find(p => p.id === presetId); if (preset) { document.getElementById('my-avatar-preview').src = preset.avatar; document.getElementById('my-persona').value = preset.persona; } closePersonaLibrary(); }

        function openPersonaEditorForCreate() { editingPersonaPresetId = null; document.getElementById('persona-editor-title').textContent = '添加人设预设'; document.getElementById('preset-avatar-preview').src = defaultAvatar; document.getElementById('preset-persona-input').value = ''; personaEditorModal.classList.add('visible'); }

        function openPersonaEditorForEdit() { const preset = state.personaPresets.find(p => p.id === editingPersonaPresetId); if (!preset) return; document.getElementById('persona-editor-title').textContent = '编辑人设预设'; document.getElementById('preset-avatar-preview').src = preset.avatar; document.getElementById('preset-persona-input').value = preset.persona; presetActionsModal.classList.remove('visible'); personaEditorModal.classList.add('visible'); }

        async function deletePersonaPreset() { const confirmed = await showCustomConfirm('删除预设', '确定要删除这个人设预设吗？此操作不可恢复。', { confirmButtonClass: 'btn-danger' }); if (confirmed && editingPersonaPresetId) { await db.personaPresets.delete(editingPersonaPresetId); state.personaPresets = state.personaPresets.filter(p => p.id !== editingPersonaPresetId); hidePresetActions(); renderPersonaLibrary(); } }

        function closePersonaEditor() { personaEditorModal.classList.remove('visible'); editingPersonaPresetId = null; }

        async function savePersonaPreset() { const avatar = document.getElementById('preset-avatar-preview').src; const persona = document.getElementById('preset-persona-input').value.trim(); if (avatar === defaultAvatar && !persona) { alert("头像和人设不能都为空哦！"); return; } if (editingPersonaPresetId) { const preset = state.personaPresets.find(p => p.id === editingPersonaPresetId); if (preset) { preset.avatar = avatar; preset.persona = persona; await db.personaPresets.put(preset); } } else { const newPreset = { id: 'preset_' + Date.now(), avatar: avatar, persona: persona }; await db.personaPresets.add(newPreset); state.personaPresets.push(newPreset); } renderPersonaLibrary(); closePersonaEditor(); }

        const batteryAlertModal = document.getElementById('battery-alert-modal');

        function showBatteryAlert(imageUrl, text) { clearTimeout(batteryAlertTimeout); document.getElementById('battery-alert-image').src = imageUrl; document.getElementById('battery-alert-text').textContent = text; batteryAlertModal.classList.add('visible'); const closeAlert = () => { batteryAlertModal.classList.remove('visible'); batteryAlertModal.removeEventListener('click', closeAlert); }; batteryAlertModal.addEventListener('click', closeAlert); batteryAlertTimeout = setTimeout(closeAlert, 2000); }

        function updateBatteryDisplay(battery) { const batteryContainer = document.getElementById('status-bar-battery'); const batteryLevelEl = batteryContainer.querySelector('.battery-level'); const batteryTextEl = batteryContainer.querySelector('.battery-text'); const level = Math.floor(battery.level * 100); batteryLevelEl.style.width = `${level}%`; batteryTextEl.textContent = `${level}%`; if (battery.charging) { batteryContainer.classList.add('charging'); } else { batteryContainer.classList.remove('charging'); } }

        function handleBatteryChange(battery) { updateBatteryDisplay(battery); const level = battery.level; if (!battery.charging) { if (level <= 0.4 && lastKnownBatteryLevel > 0.4 && !alertFlags.hasShown40) { showBatteryAlert('https://i.postimg.cc/T2yKJ0DV/40.jpg', '有点饿了，可以去找充电器惹'); alertFlags.hasShown40 = true; } if (level <= 0.2 && lastKnownBatteryLevel > 0.2 && !alertFlags.hasShown20) { showBatteryAlert('https://i.postimg.cc/qB9zbKs9/20.jpg', '赶紧的充电，要饿死了'); alertFlags.hasShown20 = true; } if (level <= 0.1 && lastKnownBatteryLevel > 0.1 && !alertFlags.hasShown10) { showBatteryAlert('https://i.postimg.cc/ThMMVfW4/10.jpg', '已阵亡，还有30秒爆炸'); alertFlags.hasShown10 = true; } } if (level > 0.4) alertFlags.hasShown40 = false; if (level > 0.2) alertFlags.hasShown20 = false; if (level > 0.1) alertFlags.hasShown10 = false; lastKnownBatteryLevel = level; }

        async function initBatteryManager() { if ('getBattery' in navigator) { try { const battery = await navigator.getBattery(); lastKnownBatteryLevel = battery.level; handleBatteryChange(battery); battery.addEventListener('levelchange', () => handleBatteryChange(battery)); battery.addEventListener('chargingchange', () => { handleBatteryChange(battery); if (battery.charging) { showBatteryAlert('https://i.postimg.cc/3NDQ0dWG/image.jpg', '窝爱泥，电量吃饱饱'); } }); } catch (err) { console.error("无法获取电池信息:", err); document.querySelector('.battery-text').textContent = 'ᗜωᗜ'; } } else { console.log("浏览器不支持电池状态API。"); document.querySelector('.battery-text').textContent = 'ᗜωᗜ'; } }

        async function renderAlbumList() {
            const albumGrid = document.getElementById('album-grid-page');
            if (!albumGrid) return;
            const albums = await db.qzoneAlbums.orderBy('createdAt').reverse().toArray();
            albumGrid.innerHTML = '';
            if (albums.length === 0) {
                albumGrid.innerHTML = '<p style="grid-column: 1 / -1; text-align: center; color: var(--text-secondary); margin-top: 50px;">你还没有创建任何相册哦~</p>';
                return;
            }
            albums.forEach(album => {
                const albumItem = document.createElement('div');
                albumItem.className = 'album-item';
                albumItem.innerHTML = `
                    <div class="album-cover" style="background-image: url(${album.coverUrl});"></div>
                    <div class="album-info">
                        <p class="album-name">${album.name}</p>
                        <p class="album-count">${album.photoCount || 0} 张</p>
                    </div>
                `;
                albumItem.addEventListener('click', () => {
                    openAlbum(album.id);
                });

                // ▼▼▼ 新增的核心代码就是这里 ▼▼▼
                addLongPressListener(albumItem, async () => {
                    const confirmed = await showCustomConfirm(
                        '删除相册',
                        `确定要删除相册《${album.name}》吗？此操作将同时删除相册内的所有照片，且无法恢复。`,
                        { confirmButtonClass: 'btn-danger' }
                    );

                    if (confirmed) {
                        // 1. 从照片表中删除该相册下的所有照片
                        await db.qzonePhotos.where('albumId').equals(album.id).delete();
                        
                        // 2. 从相册表中删除该相册本身
                        await db.qzoneAlbums.delete(album.id);
                        
                        // 3. 重新渲染相册列表
                        await renderAlbumList();
                        
                        alert('相册已成功删除。');
                    }
                });
                // ▲▲▲ 新增代码结束 ▲▲▲

                albumGrid.appendChild(albumItem);
            });
        }

        async function openAlbum(albumId) {
            state.activeAlbumId = albumId;
            await renderAlbumPhotosScreen();
            showScreen('album-photos-screen');
        }

        async function renderAlbumPhotosScreen() {
            if (!state.activeAlbumId) return;
            const photosGrid = document.getElementById('photos-grid-page');
            const headerTitle = document.getElementById('album-photos-title');
            const album = await db.qzoneAlbums.get(state.activeAlbumId);
            if (!album) {
                console.error("找不到相册:", state.activeAlbumId);
                showScreen('album-screen');
                return;
            }
            headerTitle.textContent = album.name;
            const photos = await db.qzonePhotos.where('albumId').equals(state.activeAlbumId).toArray();
            photosGrid.innerHTML = '';
            if (photos.length === 0) {
                photosGrid.innerHTML = '<p style="grid-column: 1 / -1; text-align: center; color: var(--text-secondary); margin-top: 50px;">这个相册还是空的，快上传第一张照片吧！</p>';
            } else {
                photos.forEach(photo => {
                    const photoItem = document.createElement('div');
                    photoItem.className = 'photo-item';
                    photoItem.innerHTML = `
                        <img src="${photo.url}" class="photo-thumb" alt="相册照片">
                        <button class="photo-delete-btn" data-photo-id="${photo.id}">×</button>
                    `;
                    photosGrid.appendChild(photoItem);
                });
            }
        }

// --- ↓↓↓ 从这里开始复制 ↓↓↓ ---

/**
 * 打开图片查看器
 * @param {string} clickedPhotoUrl - 用户点击的那张照片的URL
 */
async function openPhotoViewer(clickedPhotoUrl) {
    if (!state.activeAlbumId) return;

    // 1. 从数据库获取当前相册的所有照片
    const photosInAlbum = await db.qzonePhotos.where('albumId').equals(state.activeAlbumId).toArray();
    photoViewerState.photos = photosInAlbum.map(p => p.url);

    // 2. 找到被点击照片的索引
    photoViewerState.currentIndex = photoViewerState.photos.findIndex(url => url === clickedPhotoUrl);
    if (photoViewerState.currentIndex === -1) return; // 如果找不到，则不打开

    // 3. 显示模态框并渲染第一张图
    document.getElementById('photo-viewer-modal').classList.add('visible');
    renderPhotoViewer();
    photoViewerState.isOpen = true;
}

/**
 * 根据当前状态渲染查看器内容（图片和按钮）
 */
function renderPhotoViewer() {
    if (photoViewerState.currentIndex === -1) return;

    const imageEl = document.getElementById('photo-viewer-image');
    const prevBtn = document.getElementById('photo-viewer-prev-btn');
    const nextBtn = document.getElementById('photo-viewer-next-btn');
    
    // 淡出效果
    imageEl.style.opacity = 0;

    setTimeout(() => {
        // 更新图片源
        imageEl.src = photoViewerState.photos[photoViewerState.currentIndex];
        // 淡入效果
        imageEl.style.opacity = 1;
    }, 100); // 延迟一点点时间来触发CSS过渡

    // 更新按钮状态：如果是第一张，禁用“上一张”按钮
    prevBtn.disabled = photoViewerState.currentIndex === 0;
    // 如果是最后一张，禁用“下一张”按钮
    nextBtn.disabled = photoViewerState.currentIndex === photoViewerState.photos.length - 1;
}

/**
 * 显示下一张照片
 */
function showNextPhoto() {
    if (photoViewerState.currentIndex < photoViewerState.photos.length - 1) {
        photoViewerState.currentIndex++;
        renderPhotoViewer();
    }
}

/**
 * 显示上一张照片
 */
function showPrevPhoto() {
    if (photoViewerState.currentIndex > 0) {
        photoViewerState.currentIndex--;
        renderPhotoViewer();
    }
}

/**
 * 关闭图片查看器
 */
function closePhotoViewer() {
    document.getElementById('photo-viewer-modal').classList.remove('visible');
    photoViewerState.isOpen = false;
    photoViewerState.photos = [];
    photoViewerState.currentIndex = -1;
    // 清空图片，避免下次打开时闪现旧图
    document.getElementById('photo-viewer-image').src = '';
}

// --- ↑↑↑ 复制到这里结束 ↑↑↑ ---
        // ▼▼▼ 请将这个新函数粘贴到你的JS功能函数定义区 ▼▼▼
        
        /**
         * 更新动态小红点的显示
         * @param {number} count - 未读动态的数量
         */
        function updateUnreadIndicator(count) {
            unreadPostsCount = count;
            localStorage.setItem('unreadPostsCount', count); // 持久化存储

            // --- 更新底部导航栏的“动态”按钮 ---
            const navItem = document.querySelector('.nav-item[data-view="qzone-screen"]');
            
            const targetSpan = navItem.querySelector('span'); // 定位到文字 "动态"
            let indicator = navItem.querySelector('.unread-indicator');           

            if (count > 0) {
                if (!indicator) {
                    indicator = document.createElement('span');
                    indicator.className = 'unread-indicator';
                                                           targetSpan.style.position = 'relative'; // 把相对定位加在 span 上
                    targetSpan.appendChild(indicator); // 把小红点作为 span 的子元素
                    
                }
                indicator.textContent = count > 99 ? '99+' : count;
                indicator.style.display = 'block';
            } else {
                if (indicator) {
                    indicator.style.display = 'none';
                }
            }

            // --- 更新聊天界面返回列表的按钮 ---
            const backBtn = document.getElementById('back-to-list-btn');
            let backBtnIndicator = backBtn.querySelector('.unread-indicator');

            if (count > 0) {
                if (!backBtnIndicator) {
                    backBtnIndicator = document.createElement('span');
                    backBtnIndicator.className = 'unread-indicator back-btn-indicator';
                    backBtn.style.position = 'relative'; // 确保能正确定位
                    backBtn.appendChild(backBtnIndicator);
                }
                // 返回键上的小红点通常不显示数字，只显示一个点
                backBtnIndicator.style.display = 'block';
            } else {
                if (backBtnIndicator) {
                    backBtnIndicator.style.display = 'none';
                }
            }
        }
        
        // ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 将这两个新函数粘贴到你的JS功能函数定义区 ▼▼▼
function startBackgroundSimulation() {
    if (simulationIntervalId) return;
    const intervalSeconds = state.globalSettings.backgroundActivityInterval || 60;
    // 将旧的固定间隔 45000 替换为动态获取
    simulationIntervalId = setInterval(runBackgroundSimulationTick, intervalSeconds * 1000); 
}

function stopBackgroundSimulation() {
    if (simulationIntervalId) {
        clearInterval(simulationIntervalId);
        simulationIntervalId = null;
    }
}
// ▲▲▲ 粘贴结束 ▲▲▲

/**
 * 这是模拟器的“心跳”，每次定时器触发时运行
 */
function runBackgroundSimulationTick() {
    console.log("模拟器心跳 Tick...");
    if (!state.globalSettings.enableBackgroundActivity) {
        stopBackgroundSimulation();
        return;
    }
    const allSingleChats = Object.values(state.chats).filter(chat => !chat.isGroup);

    if (allSingleChats.length === 0) return;

    allSingleChats.forEach(chat => {
        // 【核心修正】将两种状态检查分离开，逻辑更清晰

        // 检查1：处理【被用户拉黑】的角色
        if (chat.relationship?.status === 'blocked_by_user') {
            const blockedTimestamp = chat.relationship.blockedTimestamp;
            // 安全检查：确保有拉黑时间戳
            if (!blockedTimestamp) {
                console.warn(`角色 "${chat.name}" 状态为拉黑，但缺少拉黑时间戳，跳过处理。`);
                return; // 跳过这个角色，继续下一个
            }

            const blockedDuration = Date.now() - blockedTimestamp;
            const cooldownMilliseconds = (state.globalSettings.blockCooldownHours || 1) * 60 * 60 * 1000;

            console.log(`检查角色 "${chat.name}"：已拉黑 ${Math.round(blockedDuration/1000/60)}分钟，冷静期需 ${cooldownMilliseconds/1000/60}分钟。`); // 添加日志

            // 【核心修改】移除了随机概率，只要冷静期一过，就触发！
            if (blockedDuration > cooldownMilliseconds) {
                console.log(`角色 "${chat.name}" 的冷静期已过，触发“反思”并申请好友事件...`);
                
                // 【重要】为了防止在AI响应前重复触发，我们在触发后立刻更新状态
                chat.relationship.status = 'pending_system_reflection'; // 设置一个临时的、防止重复触发的状态
                
                triggerAiFriendApplication(chat.id);
            }
        }
        // 检查2：处理【好友关系】的正常后台活动
        else if (chat.relationship?.status === 'friend' && chat.id !== state.activeChatId) {
            // 这里的随机触发逻辑保持不变，因为我们不希望所有好友同时行动
            if (Math.random() < 0.20) {
                console.log(`角色 "${chat.name}" 被唤醒，准备独立行动...`);
                triggerInactiveAiAction(chat.id);
            }
        }
    });
}

async function triggerInactiveAiAction(chatId) {
    const chat = state.chats[chatId];
    if (!chat) return;

    const { proxyUrl, apiKey, model } = state.apiConfig;
    if (!proxyUrl || !apiKey || !model) return;

    const now = new Date();
    const currentTime = now.toLocaleTimeString('zh-CN', { hour: 'numeric', minute: 'numeric', hour12: true });
    const userNickname = state.qzoneSettings.nickname;
const countdownContext = await getCountdownContext(); // <--- 就是这一行

    const lastUserMessage = chat.history.filter(m => m.role === 'user' && !m.isHidden).slice(-1)[0];
    const lastAiMessage = chat.history.filter(m => m.role === 'assistant' && !m.isHidden).slice(-1)[0];
    let recentContextSummary = "你们最近没有聊过天。";
    if (lastUserMessage) {
        recentContextSummary = `用户 (${userNickname}) 最后对你说：“${String(lastUserMessage.content).substring(0, 50)}...”。`;
    }
    if (lastAiMessage) {
        recentContextSummary += `\n你最后对用户说：“${String(lastAiMessage.content).substring(0, 50)}...”。`;
    }

    // ▼▼▼ 在这里添加下面的代码 ▼▼▼
    let worldBookContent = '';
    if (chat.settings.linkedWorldBookIds && chat.settings.linkedWorldBookIds.length > 0) {
        const linkedContents = chat.settings.linkedWorldBookIds.map(bookId => {
            const worldBook = state.worldBooks.find(wb => wb.id === bookId);
            return worldBook && worldBook.content ? `\n\n## 世界书: ${worldBook.name}\n${worldBook.content}` : '';
        }).filter(Boolean).join('');
        if (linkedContents) {
            worldBookContent = `\n\n# 核心世界观设定 (你必须严格遵守)\n${linkedContents}\n`;
        }
    }
    // ▲▲▲ 添加结束 ▲▲▲

    const systemPrompt = `
# 你的任务
你现在扮演一个名为"${chat.name}"的角色。你已经有一段时间没有和用户（${userNickname}）互动了，现在你有机会【主动】做点什么，来表现你的个性和独立生活。这是一个秘密的、后台的独立行动。

# 你的可选行动 (请根据你的人设【选择一项】执行):
1.  **改变状态**: 去做点别的事情，然后给用户发条消息。
2.  **发布动态**: 分享你的心情或想法到“动态”区。
3.  **与动态互动**: 去看看别人的帖子并进行评论或点赞。
4.  **发起视频通话**: 如果你觉得时机合适，可以主动给用户打一个视频电话。
5.  **庆祝或提醒约定**: 查看你的备忘录。如果今天有约定，就给用户发去祝福；如果约定快到了，就主动提醒一下。

# 指令格式 (你的回复【必须】是包含一个对象的JSON数组):
-   **发普通消息**: \`[{"type": "text", "content": "你想对用户说的话..."}]\`
-   **发消息+更新状态**: \`[{"type": "update_status", "status_text": "正在做的事", "is_busy": true}, {"type": "text", "content": "你想对用户说的话..."}]\`
-   **发说说**: \`[{"type": "qzone_post", "postType": "shuoshuo", "content": "动态的文字内容..."}]\`
- **发布文字图**: \`{"type": "qzone_post", "postType": "text_image", "publicText": "(可选)动态的公开文字", "hiddenContent": "对于图片的具体描述..."}\`
-   **评论**: \`[{"type": "qzone_comment", "postId": 123, "commentText": "你的评论内容"}]\`
-   **点赞**: \`[{"type": "qzone_like", "postId": 456}]\`
-   **打视频**: \`[{"type": "video_call_request"}]\`

# 供你决策的参考信息：
-   **你的角色设定**: ${chat.settings.aiPersona}
${countdownContext} // <--- 把备忘录加在这里
${worldBookContent} // <--【核心】在这里注入世界书内容
-   **当前时间**: ${currentTime}
-   **你们最后的对话摘要**: ${recentContextSummary}
-   **【重要】最近的动态列表**: 这个列表会标注 **[你已点赞]** 或 **[你已评论]**。请**优先**与你**尚未互动过**的动态进行交流。`;

    // 【核心修复】在这里构建 messagesPayload
    const messagesPayload = [];
    messagesPayload.push({ role: 'system', content: systemPrompt });

try {
    const allRecentPosts = await db.qzonePosts.orderBy('timestamp').reverse().limit(3).toArray();
    // 【核心修改】在这里插入过滤步骤
    const visiblePosts = filterVisiblePostsForAI(allRecentPosts, chat);
    
    const aiName = chat.name;
    
    let dynamicContext = ""; 
    if (visiblePosts.length > 0) {
        let postsContext = "\n\n# 最近的动态列表 (供你参考和评论):\n";
        for (const post of visiblePosts) {
                let authorName = post.authorId === 'user' ? userNickname : (state.chats[post.authorId]?.name || '一位朋友');
                let interactionStatus = '';
                if (post.likes && post.likes.includes(aiName)) interactionStatus += " [你已点赞]";
                if (post.comments && post.comments.some(c => c.commenterName === aiName)) interactionStatus += " [你已评论]";
                
                postsContext += `- (ID: ${post.id}) 作者: ${authorName}, 内容: "${(post.publicText || post.content || "图片动态").substring(0, 30)}..."${interactionStatus}\n`;
            }
            dynamicContext = postsContext;
        }

        // 【核心修复】将所有动态信息作为一条 user 消息发送
        messagesPayload.push({
            role: 'user',
            content: `[系统指令：请根据你在 system prompt 中读到的规则和以下最新信息，开始你的独立行动。]\n${dynamicContext}`
        });
        
        console.log("正在为后台活动发送API请求，Payload:", JSON.stringify(messagesPayload, null, 2)); // 添加日志，方便调试

            // 发送请求
            let isGemini = proxyUrl === GEMINI_API_URL;
            let geminiConfig = toGeminiRequestData(model, apiKey, systemPrompt, messagesPayload, isGemini);

            const response = isGemini 
                ? await fetch(geminiConfig.url, geminiConfig.data) 
                : await fetch(`${proxyUrl}/v1/chat/completions`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                    body: JSON.stringify({
                        model: model,
                        messages: messagesPayload,
                        temperature: 0.9,
                    })
                });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`API请求失败: ${response.status} - ${JSON.stringify(errorData)}`);
            }

            const data = await response.json();

            // 【核心修复】使用可选链操作符 (?.) 安全地提取消息内容
            const aiResponseContent = isGemini
                ? data?.candidates?.[0]?.content?.parts?.[0]?.text
                : data?.choices?.[0]?.message?.content;

            // 现在，我们再对提取出的内容进行检查，这样就万无一失了
            if (!aiResponseContent) {
                console.warn(`API为空回或格式不正确（可能因安全设置被拦截），角色 "${chat.name}" 的本次后台活动跳过。返回数据:`, data);
                return;
            }

            // 使用安全获取到的内容进行解析
            const responseArray = parseAiResponse(aiResponseContent);

        
        // 后续处理AI返回指令的逻辑保持不变...
        for (const action of responseArray) {
            if (!action) continue;

            if (action.type === 'update_status' && action.status_text) {
                chat.status.text = action.status_text;
                chat.status.isBusy = action.is_busy || false;
                chat.status.lastUpdate = Date.now();
                await db.chats.put(chat);
                renderChatList();
            }
            if (action.type === 'text' && action.content) {
                const aiMessage = { role: 'assistant', content: String(action.content), timestamp: Date.now() };

chat.unreadCount = (chat.unreadCount || 0) + 1;
                chat.history.push(aiMessage);
                await db.chats.put(chat);
                showNotification(chatId, aiMessage.content);
                renderChatList();
                console.log(`后台活动: 角色 "${chat.name}" 主动发送了消息: ${aiMessage.content}`);
            }
if (action.type === 'qzone_post') {
    const newPost = { 
        type: action.postType, 
        content: action.content || '', 
        publicText: action.publicText || '', 
        hiddenContent: action.hiddenContent || '', 
        timestamp: Date.now(), 
        authorId: chatId, 
        authorGroupId: chat.groupId, // 【核心新增】记录作者的分组ID
        visibleGroupIds: null 
    };
    await db.qzonePosts.add(newPost);
                updateUnreadIndicator(unreadPostsCount + 1);
                console.log(`后台活动: 角色 "${chat.name}" 发布了动态`);
            } else if (action.type === 'qzone_comment') {
                const post = await db.qzonePosts.get(parseInt(action.postId));
                if (post) {
                    if (!post.comments) post.comments = [];
                    post.comments.push({ commenterName: chat.name, text: action.commentText, timestamp: Date.now() });
                    await db.qzonePosts.update(post.id, { comments: post.comments });
                    updateUnreadIndicator(unreadPostsCount + 1);
                    console.log(`后台活动: 角色 "${chat.name}" 评论了动态 #${post.id}`);
                }
            } else if (action.type === 'qzone_like') {
                const post = await db.qzonePosts.get(parseInt(action.postId));
                if (post) {
                    if (!post.likes) post.likes = [];
                    if (!post.likes.includes(chat.name)) {
                        post.likes.push(chat.name);
                        await db.qzonePosts.update(post.id, { likes: post.likes });
                        updateUnreadIndicator(unreadPostsCount + 1);
                        console.log(`后台活动: 角色 "${chat.name}" 点赞了动态 #${post.id}`);
                    }
                }
            } else if (action.type === 'video_call_request') {
                if (!videoCallState.isActive && !videoCallState.isAwaitingResponse) {
                    videoCallState.isAwaitingResponse = true; 
                    videoCallState.activeChatId = chatId; // <--- 修改成这样！
                    showIncomingCallModal(chatId); // <--- 修改成这样，把 chatId 传进去
                    console.log(`后台活动: 角色 "${chat.name}" 发起了视频通话请求`);
                }
            }
        }
    } catch (error) {
        console.error(`角色 "${chat.name}" 的独立行动失败:`, error);
    }
}

// ▼▼▼ 请用这个【终极修正版】函数，完整替换掉你旧的 applyScopedCss 函数 ▼▼▼

/**
 * 将用户自定义的CSS安全地应用到指定的作用域
 * @param {string} cssString 用户输入的原始CSS字符串
 * @param {string} scopeId 应用样式的作用域ID (例如 '#chat-messages' 或 '#settings-preview-area')
 * @param {string} styleTagId 要操作的 <style> 标签的ID
 */
function applyScopedCss(cssString, scopeId, styleTagId) {
    const styleTag = document.getElementById(styleTagId);
    if (!styleTag) return;
    
    if (!cssString || cssString.trim() === '') {
        styleTag.innerHTML = '';
        return;
    }
    
    // 增强作用域处理函数 - 专门解决.user和.ai样式冲突问题
    const scopedCss = cssString
        .replace(/\s*\.message-bubble\.user\s+([^{]+\{)/g, `${scopeId} .message-bubble.user $1`)
        .replace(/\s*\.message-bubble\.ai\s+([^{]+\{)/g, `${scopeId} .message-bubble.ai $1`)
        .replace(/\s*\.message-bubble\s+([^{]+\{)/g, `${scopeId} .message-bubble $1`);
    
    styleTag.innerHTML = scopedCss;
}

// ▼▼▼ 请用这个【修正版】函数，完整替换掉旧的 updateSettingsPreview 函数 ▼▼▼

function updateSettingsPreview() {
    if (!state.activeChatId) return;
    const chat = state.chats[state.activeChatId];
    const previewArea = document.getElementById('settings-preview-area');
    if (!previewArea) return;

    // 1. 获取当前设置的值
    const selectedTheme = document.querySelector('input[name="theme-select"]:checked')?.value || 'default';
    const fontSize = document.getElementById('font-size-slider').value;
    const customCss = document.getElementById('custom-css-input').value;
    const background = chat.settings.background; // 直接获取背景设置

    // 2. 更新预览区的基本样式
    previewArea.dataset.theme = selectedTheme;
    previewArea.style.setProperty('--chat-font-size', `${fontSize}px`);
    
    // --- 【核心修正】直接更新预览区的背景样式 ---
    if (background && background.startsWith('data:image')) {
        previewArea.style.backgroundImage = `url(${background})`;
        previewArea.style.backgroundColor = 'transparent'; // 如果有图片，背景色设为透明
    } else {
        previewArea.style.backgroundImage = 'none'; // 如果没有图片，移除图片背景
        // 如果背景是颜色值或渐变（非图片），则直接应用
        previewArea.style.background = background || '#f0f2f5';
    }

    // 3. 渲染模拟气泡
    previewArea.innerHTML = ''; 

    // 创建“对方”的气泡
    // 注意：我们将一个虚拟的 timestamp 传入，以防有CSS依赖于它
    const aiMsg = { role: 'ai', content: '对方消息预览', timestamp: 1, senderName: chat.name };
    const aiBubble = createMessageElement(aiMsg, chat);
    if(aiBubble) previewArea.appendChild(aiBubble);

    // 创建“我”的气泡
    const userMsg = { role: 'user', content: '我的消息预览', timestamp: 2 };
    const userBubble = createMessageElement(userMsg, chat);
    if(userBubble) previewArea.appendChild(userBubble);
    
    // 4. 应用自定义CSS到预览区
    applyScopedCss(customCss, '#settings-preview-area', 'preview-bubble-style');
}

// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 请将这些【新函数】粘贴到JS功能函数定义区 ▼▼▼

async function openGroupManager() {
    await renderGroupList();
    document.getElementById('group-management-modal').classList.add('visible');
}

async function renderGroupList() {
    const listEl = document.getElementById('existing-groups-list');
    const groups = await db.qzoneGroups.toArray();
    listEl.innerHTML = '';
    if (groups.length === 0) {
        listEl.innerHTML = '<p style="text-align: center; color: var(--text-secondary);">还没有任何分组</p>';
    }
    groups.forEach(group => {
        const item = document.createElement('div');
        item.className = 'existing-group-item';
        item.innerHTML = `
            <span class="group-name">${group.name}</span>
            <span class="delete-group-btn" data-id="${group.id}">×</span>
        `;
        listEl.appendChild(item);
    });
}

// ▼▼▼ 请用这个【修正后】的函数，完整替换旧的 addNewGroup 函数 ▼▼▼
async function addNewGroup() {
    const input = document.getElementById('new-group-name-input');
    const name = input.value.trim();
    if (!name) {
        alert('分组名不能为空！');
        return;
    }

    // 【核心修正】在添加前，先检查分组名是否已存在
    const existingGroup = await db.qzoneGroups.where('name').equals(name).first();
    if (existingGroup) {
        alert(`分组 "${name}" 已经存在了，换个名字吧！`);
        return;
    }
    // 【修正结束】

    await db.qzoneGroups.add({ name });
    input.value = '';
    await renderGroupList();
}
// ▲▲▲ 替换结束 ▲▲▲

async function deleteGroup(groupId) {
    const confirmed = await showCustomConfirm('确认删除', '删除分组后，该组内的好友将变为“未分组”。确定要删除吗？', { confirmButtonClass: 'btn-danger' });
    if (confirmed) {
        await db.qzoneGroups.delete(groupId);
        // 将属于该分组的好友的 groupId 设为 null
        const chatsToUpdate = await db.chats.where('groupId').equals(groupId).toArray();
        for (const chat of chatsToUpdate) {
            chat.groupId = null;
            await db.chats.put(chat);
            if(state.chats[chat.id]) state.chats[chat.id].groupId = null;
        }
        await renderGroupList();
    }
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 请将这【一整块新函数】粘贴到JS功能函数定义区的末尾 ▼▼▼

/**
 * 当长按消息时，显示操作菜单
 * @param {number} timestamp - 被长按消息的时间戳
 */
function showMessageActions(timestamp) {
    // 如果已经在多选模式，则不弹出菜单
    if (isSelectionMode) return;
    
    activeMessageTimestamp = timestamp;
    document.getElementById('message-actions-modal').classList.add('visible');
}

/**
 * 隐藏消息操作菜单
 */
function hideMessageActions() {
    document.getElementById('message-actions-modal').classList.remove('visible');
    activeMessageTimestamp = null;
}

// ▼▼▼ 用这个【已更新】的版本，替换旧的 openMessageEditor 函数 ▼▼▼
async function openMessageEditor() {
    if (!activeMessageTimestamp) return;

    const timestampToEdit = activeMessageTimestamp;
    const chat = state.chats[state.activeChatId];
    const message = chat.history.find(m => m.timestamp === timestampToEdit);
    if (!message) return;

    hideMessageActions(); 

    let contentForEditing;
    // 【核心修正】将 share_link 也加入特殊类型判断
    const isSpecialType = message.type && ['voice_message', 'ai_image', 'transfer', 'share_link'].includes(message.type);

    if (isSpecialType) {
        let fullMessageObject = { type: message.type };
        if (message.type === 'voice_message') fullMessageObject.content = message.content;
        else if (message.type === 'ai_image') fullMessageObject.description = message.content; 
        else if (message.type === 'transfer') {
            fullMessageObject.amount = message.amount;
            fullMessageObject.note = message.note;
        } 
        // 【核心修正】处理分享链接类型的消息
        else if (message.type === 'share_link') {
            fullMessageObject.title = message.title;
            fullMessageObject.description = message.description;
            fullMessageObject.source_name = message.source_name;
            fullMessageObject.content = message.content;
        }
        contentForEditing = JSON.stringify(fullMessageObject, null, 2);
    } else if (typeof message.content === 'object') {
        contentForEditing = JSON.stringify(message.content, null, 2);
    } else {
        contentForEditing = message.content;
    }

    // 【核心修改1】在这里添加 'link' 模板
    const templates = {
        voice: { type: 'voice_message', content: '在这里输入语音内容' },
        image: { type: 'ai_image', description: '在这里输入图片描述' },
        transfer: { type: 'transfer', amount: 5.20, note: '一点心意' },
        link: { type: 'share_link', title: '文章标题', description: '文章摘要...', source_name: '来源网站', content: '文章完整内容...' }
    };

    // 【核心修改2】在这里添加新的“链接”按钮
    const helpersHtml = `
        <div class="format-helpers">
            <button class="format-btn" data-template='${JSON.stringify(templates.voice)}'>语音</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.image)}'>图片</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.transfer)}'>转账</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.link)}'>链接</button>
        </div>
    `;

    const newContent = await showCustomPrompt(
        '编辑消息', 
        '在此修改，或点击上方按钮使用格式模板...',
        contentForEditing, 
        'textarea',
        helpersHtml
    );

    if (newContent !== null) {
        // 【核心修正】这里调用的应该是 saveEditedMessage，而不是 saveAdvancedEditor
        await saveEditedMessage(timestampToEdit, newContent, true);
    }
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 复制消息的文本内容到剪贴板
 */
async function copyMessageContent() {
    if (!activeMessageTimestamp) return;
    const chat = state.chats[state.activeChatId];
    const message = chat.history.find(m => m.timestamp === activeMessageTimestamp);
    if (!message) return;

    let textToCopy;
    if (typeof message.content === 'object') {
        textToCopy = JSON.stringify(message.content);
    } else {
        textToCopy = String(message.content);
    }

    try {
        await navigator.clipboard.writeText(textToCopy);
        await showCustomAlert('复制成功', '消息内容已复制到剪贴板。');
    } catch (err) {
        await showCustomAlert('复制失败', '无法访问剪贴板。');
    }
    
    hideMessageActions();
}

// ▼▼▼ 用这个【已更新】的版本，替换旧的 createMessageEditorBlock 函数 ▼▼▼
/**
 * 创建一个可编辑的消息块（包含文本框、格式助手和删除按钮）
 * @param {string} initialContent - 文本框的初始内容
 * @returns {HTMLElement} - 创建好的DOM元素
 */
function createMessageEditorBlock(initialContent = '') {
    const block = document.createElement('div');
    block.className = 'message-editor-block';

    // 【核心修改1】在这里添加 'link' 模板
    const templates = {
        voice: { type: 'voice_message', content: '在这里输入语音内容' },
        image: { type: 'ai_image', description: '在这里输入图片描述' },
        transfer: { type: 'transfer', amount: 5.20, note: '一点心意' },
        link: { type: 'share_link', title: '文章标题', description: '文章摘要...', source_name: '来源网站', content: '文章完整内容...' }
    };

    block.innerHTML = `
        <button class="delete-block-btn" title="删除此条">×</button>
        <textarea>${initialContent}</textarea>
        <div class="format-helpers">
            <button class="format-btn" data-template='${JSON.stringify(templates.voice)}'>语音</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.image)}'>图片</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.transfer)}'>转账</button>
            <!-- 【核心修改2】在这里添加新的“链接”按钮 -->
            <button class="format-btn" data-template='${JSON.stringify(templates.link)}'>链接</button>
        </div>
    `;

    // 绑定删除按钮事件
    block.querySelector('.delete-block-btn').addEventListener('click', () => {
        // 确保至少保留一个编辑块
        if (document.querySelectorAll('.message-editor-block').length > 1) {
            block.remove();
        } else {
            alert('至少需要保留一条消息。');
        }
    });

    // 绑定格式助手按钮事件
    block.querySelectorAll('.format-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            const templateStr = btn.dataset.template;
            const textarea = block.querySelector('textarea');
            if (templateStr && textarea) {
                try {
                    const templateObj = JSON.parse(templateStr);
                    textarea.value = JSON.stringify(templateObj, null, 2);
                    textarea.focus();
                } catch(e) { console.error("解析格式模板失败:", e); }
            }
        });
    });

    return block;
}
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【全新升级版】请用此函数完整替换旧的 openAdvancedMessageEditor ▼▼▼
/**
 * 打开全新的、可视化的多消息编辑器，并动态绑定其所有按钮事件
 */
function openAdvancedMessageEditor() {
    if (!activeMessageTimestamp) return;

    // 1. 【核心】在关闭旧菜单前，将需要的时间戳捕获到局部变量中
    const timestampToEdit = activeMessageTimestamp;

    const chat = state.chats[state.activeChatId];
    const message = chat.history.find(m => m.timestamp === timestampToEdit);
    if (!message) return;

    // 2. 现在可以安全地关闭旧菜单了，因为它不会影响我们的局部变量
    hideMessageActions(); 

    const editorModal = document.getElementById('message-editor-modal');
    const editorContainer = document.getElementById('message-editor-container');
    editorContainer.innerHTML = ''; 

    // 3. 准备初始内容
    let initialContent;
    const isSpecialType = message.type && ['voice_message', 'ai_image', 'transfer'].includes(message.type);
    if (isSpecialType) {
        let fullMessageObject = { type: message.type };
        if (message.type === 'voice_message') fullMessageObject.content = message.content;
        else if (message.type === 'ai_image') fullMessageObject.description = message.content;
        else if (message.type === 'transfer') {
            fullMessageObject.amount = message.amount;
            fullMessageObject.note = message.note;
        }
        initialContent = JSON.stringify(fullMessageObject, null, 2);
    } else if (typeof message.content === 'object') {
        initialContent = JSON.stringify(message.content, null, 2);
    } else {
        initialContent = message.content;
    }

    const firstBlock = createMessageEditorBlock(initialContent);
    editorContainer.appendChild(firstBlock);

    // 4. 【核心】动态绑定所有控制按钮的事件
    // 为了防止事件重复绑定，我们使用克隆节点的方法来清除旧监听器
    const addBtn = document.getElementById('add-message-editor-block-btn');
    const newAddBtn = addBtn.cloneNode(true);
    addBtn.parentNode.replaceChild(newAddBtn, addBtn);
    newAddBtn.addEventListener('click', () => {
        const newBlock = createMessageEditorBlock();
        editorContainer.appendChild(newBlock);
        newBlock.querySelector('textarea').focus();
    });

    const cancelBtn = document.getElementById('cancel-advanced-editor-btn');
    const newCancelBtn = cancelBtn.cloneNode(true);
    cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn);
    newCancelBtn.addEventListener('click', () => {
        editorModal.classList.remove('visible');
    });

    const saveBtn = document.getElementById('save-advanced-editor-btn');
    const newSaveBtn = saveBtn.cloneNode(true);
    saveBtn.parentNode.replaceChild(newSaveBtn, saveBtn);
    // 将捕获到的时间戳，直接绑定给这一次的保存点击事件
    newSaveBtn.addEventListener('click', () => {
        saveEditedMessage(timestampToEdit); 
    });

    // 5. 最后，显示模态框
    editorModal.classList.add('visible');
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 解析编辑后的文本，并返回一个标准化的消息片段对象
 * @param {string} text - 用户在编辑框中输入的文本
 * @returns {object} - 一个包含 type, content, 等属性的对象
 */
function parseEditedContent(text) {
    const trimmedText = text.trim();

    // 1. 尝试解析为JSON对象（用于修复语音、转账等格式）
    if (trimmedText.startsWith('{') && trimmedText.endsWith('}')) {
        try {
            const parsed = JSON.parse(trimmedText);
            // 必须包含 type 属性才认为是有效格式
            if (parsed.type) {
                return parsed;
            }
        } catch (e) { /* 解析失败，继续往下走 */ }
    }
    
    // 2. 尝试解析为表情包
    if (STICKER_REGEX.test(trimmedText)) {
        // 对于编辑的表情，我们暂时无法知道其`meaning`，所以只存URL
        return { type: 'sticker', content: trimmedText };
    }

    // 3. 否则，视为普通文本消息
    return { type: 'text', content: trimmedText };
}


// ▼▼▼ 请用这个【已彻底修复】的函数，完整替换你现有的 saveEditedMessage 函数 ▼▼▼

async function saveEditedMessage(timestamp, simpleContent = null) {
    if (!timestamp) return;

    const chat = state.chats[state.activeChatId];
    const messageIndex = chat.history.findIndex(m => m.timestamp === timestamp);
    if (messageIndex === -1) return;

    let newMessages = [];

    // 判断是来自高级编辑器还是简单编辑器
    if (simpleContent !== null) {
        // --- 来自简单编辑器 ---
        const rawContent = simpleContent.trim();
        if (rawContent) {
            const parsedResult = parseEditedContent(rawContent);
            const newMessage = {
                role: chat.history[messageIndex].role,
                senderName: chat.history[messageIndex].senderName,
                // 注意：这里我们暂时不设置时间戳
                content: parsedResult.content || '',
            };
            if (parsedResult.type && parsedResult.type !== 'text') newMessage.type = parsedResult.type;
            if (parsedResult.meaning) newMessage.meaning = parsedResult.meaning;
            if (parsedResult.amount) newMessage.amount = parsedResult.amount;
            if (parsedResult.note) newMessage.note = parsedResult.note;
            if (parsedResult.title) newMessage.title = parsedResult.title;
            if (parsedResult.description) newMessage.description = parsedResult.description;
            if (parsedResult.source_name) newMessage.source_name = parsedResult.source_name;
            if (parsedResult.description && parsedResult.type === 'ai_image') {
                 newMessage.content = parsedResult.description;
            }

            newMessages.push(newMessage);
        }
    } else {
        // --- 来自高级编辑器 ---
        const editorContainer = document.getElementById('message-editor-container');
        const editorBlocks = editorContainer.querySelectorAll('.message-editor-block');

        for (const block of editorBlocks) {
            const textarea = block.querySelector('textarea');
            const rawContent = textarea.value.trim();
            if (!rawContent) continue;

            const parsedResult = parseEditedContent(rawContent);
            const newMessage = {
                role: chat.history[messageIndex].role,
                senderName: chat.history[messageIndex].senderName,
                // 同样，这里我们先不分配时间戳
                content: parsedResult.content || '',
            };
            
            if (parsedResult.type && parsedResult.type !== 'text') newMessage.type = parsedResult.type;
            if (parsedResult.meaning) newMessage.meaning = parsedResult.meaning;
            if (parsedResult.amount) newMessage.amount = parsedResult.amount;
            if (parsedResult.note) newMessage.note = parsedResult.note;
            if (parsedResult.title) newMessage.title = parsedResult.title;
            if (parsedResult.description) newMessage.description = parsedResult.description;
            if (parsedResult.source_name) newMessage.source_name = parsedResult.source_name;
            if (parsedResult.description && parsedResult.type === 'ai_image') {
                 newMessage.content = parsedResult.description;
            }

            newMessages.push(newMessage);
        }
    }
    
    if (newMessages.length === 0) {
        document.getElementById('message-editor-modal').classList.remove('visible');
        return; // 如果是空消息，直接返回，不执行删除操作
    }

    // ★★★★★【核心修复逻辑就在这里】★★★★★

    // 1. 使用 splice 将旧消息替换为新消息（此时新消息还没有时间戳）
    chat.history.splice(messageIndex, 1, ...newMessages);

    // 2. 确定重新分配时间戳的起点
    // 我们从被编辑的消息的原始时间戳开始
    let reassignTimestamp = timestamp;

    // 3. 从被修改的位置开始，遍历所有后续的消息
    for (let i = messageIndex; i < chat.history.length; i++) {
        // 4. 为每一条消息（包括新插入的）分配一个新的、唯一的、连续的时间戳
        chat.history[i].timestamp = reassignTimestamp;

        // 5. 将时间戳+1，为下一条消息做准备
        reassignTimestamp++; 
    }
    // ★★★★★【修复结束】★★★★★

    await db.chats.put(chat);

    // 关闭可能打开的模态框并刷新UI
    document.getElementById('message-editor-modal').classList.remove('visible');
    renderChatInterface(state.activeChatId);
    await showCustomAlert('成功', '消息已更新！');
}

// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 请将这【一整块新函数】粘贴到JS功能函数定义区的末尾 ▼▼▼

/**
 * 当点击“…”时，显示动态操作菜单
 * @param {number} postId - 被操作的动态的ID
 */
function showPostActions(postId) {
    activePostId = postId;
    document.getElementById('post-actions-modal').classList.add('visible');
}

/**
 * 隐藏动态操作菜单
 */
function hidePostActions() {
    document.getElementById('post-actions-modal').classList.remove('visible');
    activePostId = null;
}

/**
 * 打开动态编辑器
 */
async function openPostEditor() {
    if (!activePostId) return;

    const postIdToEdit = activePostId;
    const post = await db.qzonePosts.get(postIdToEdit);
    if (!post) return;

    hidePostActions();

    // 忠于原文：构建出最原始的文本形态供编辑
    let contentForEditing;
    if (post.type === 'shuoshuo') {
        contentForEditing = post.content;
    } else {
        // 对于图片和文字图，我们构建一个包含所有信息的对象
        const postObject = {
            type: post.type,
            publicText: post.publicText || '',
        };
        if (post.type === 'image_post') {
            postObject.imageUrl = post.imageUrl;
            postObject.imageDescription = post.imageDescription;
        } else if (post.type === 'text_image') {
            postObject.hiddenContent = post.hiddenContent;
        }
        contentForEditing = JSON.stringify(postObject, null, 2);
    }
    
    // 构建格式助手按钮
    const templates = {
        shuoshuo: "在这里输入说说的内容...", // 对于说说，我们直接替换为纯文本
        image: { type: 'image_post', publicText: '', imageUrl: 'https://...', imageDescription: '' },
        text_image: { type: 'text_image', publicText: '', hiddenContent: '' }
    };
    
    const helpersHtml = `
        <div class="format-helpers">
            <button class="format-btn" data-type="text">说说</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.image)}'>图片动态</button>
            <button class="format-btn" data-template='${JSON.stringify(templates.text_image)}'>文字图</button>
        </div>
    `;

    const newContent = await showCustomPrompt(
        '编辑动态',
        '在此修改内容...',
        contentForEditing,
        'textarea',
        helpersHtml
    );
    
    // 【特殊处理】为说说的格式助手按钮添加不同的行为
    // 我们需要在模态框出现后，再给它绑定事件
    setTimeout(() => {
        const shuoshuoBtn = document.querySelector('#custom-modal-body .format-btn[data-type="text"]');
        if(shuoshuoBtn) {
            shuoshuoBtn.addEventListener('click', () => {
                const input = document.getElementById('custom-prompt-input');
                input.value = templates.shuoshuo;
                input.focus();
            });
        }
    }, 100);

    if (newContent !== null) {
        await saveEditedPost(postIdToEdit, newContent);
    }
}

/**
 * 保存编辑后的动态
 * @param {number} postId - 要保存的动态ID
 * @param {string} newRawContent - 从编辑器获取的新内容
 */
async function saveEditedPost(postId, newRawContent) {
    const post = await db.qzonePosts.get(postId);
    if (!post) return;

    const trimmedContent = newRawContent.trim();
    
    // 尝试解析为JSON，如果失败，则认为是纯文本（说说）
    try {
        const parsed = JSON.parse(trimmedContent);
        // 更新帖子属性
        post.type = parsed.type || 'image_post';
        post.publicText = parsed.publicText || '';
        post.imageUrl = parsed.imageUrl || '';
        post.imageDescription = parsed.imageDescription || '';
        post.hiddenContent = parsed.hiddenContent || '';
        post.content = ''; // 清空旧的说说内容字段
    } catch (e) {
        // 解析失败，认为是说说
        post.type = 'shuoshuo';
        post.content = trimmedContent;
        // 清空其他类型的字段
        post.publicText = '';
        post.imageUrl = '';
        post.imageDescription = '';
        post.hiddenContent = '';
    }
    
    await db.qzonePosts.put(post);
    await renderQzonePosts(); // 重新渲染列表
    await showCustomAlert('成功', '动态已更新！');
}

/**
 * 复制动态内容
 */
async function copyPostContent() {
    if (!activePostId) return;
    const post = await db.qzonePosts.get(activePostId);
    if (!post) return;
    
    let textToCopy = post.content || post.publicText || post.hiddenContent || post.imageDescription || "（无文字内容）";
    
    try {
        await navigator.clipboard.writeText(textToCopy);
        await showCustomAlert('复制成功', '动态内容已复制到剪贴板。');
    } catch (err) {
        await showCustomAlert('复制失败', '无法访问剪贴板。');
    }
    
    hidePostActions();
}

// ▼▼▼ 【全新】创建群聊与拉人功能核心函数 ▼▼▼
let selectedContacts = new Set();

async function openContactPickerForGroupCreate() {
    selectedContacts.clear(); // 清空上次选择

    // 【核心修复】在这里，我们为“完成”按钮明确绑定“创建群聊”的功能
    const confirmBtn = document.getElementById('confirm-contact-picker-btn');
    // 使用克隆节点技巧，清除掉之前可能绑定的任何其他事件（比如“添加成员”）
    const newConfirmBtn = confirmBtn.cloneNode(true);
    confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
    // 重新绑定正确的“创建群聊”函数
    newConfirmBtn.addEventListener('click', handleCreateGroup);

    await renderContactPicker();
    showScreen('contact-picker-screen');
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 渲染联系人选择列表
 */
async function renderContactPicker() {
    const listEl = document.getElementById('contact-picker-list');
    listEl.innerHTML = '';

    // 只选择单聊角色作为群成员候选
    const contacts = Object.values(state.chats).filter(chat => !chat.isGroup);

    if (contacts.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color:#8a8a8a; margin-top:50px;">还没有可以拉进群的联系人哦~</p>';
        return;
    }

    contacts.forEach(contact => {
        const item = document.createElement('div');
        item.className = 'contact-picker-item';
        item.dataset.contactId = contact.id;
        item.innerHTML = `
            <div class="checkbox"></div>
            <img src="${contact.settings.aiAvatar || defaultAvatar}" class="avatar">
            <span class="name">${contact.name}</span>
        `;
        listEl.appendChild(item);
    });

    updateContactPickerConfirmButton();
}

/**
 * 更新“完成”按钮的计数
 */
function updateContactPickerConfirmButton() {
    const btn = document.getElementById('confirm-contact-picker-btn');
    btn.textContent = `完成(${selectedContacts.size})`;
    btn.disabled = selectedContacts.size < 2; // 至少需要2个人才能创建群聊
}

/**
 * 【重构版】处理创建群聊的最终逻辑
 */
async function handleCreateGroup() {
    if (selectedContacts.size < 2) {
        alert("创建群聊至少需要选择2个联系人。");
        return;
    }

    const groupName = await showCustomPrompt('设置群名', '请输入群聊的名字', '我们的群聊');
    if (!groupName || !groupName.trim()) return;

    const newChatId = 'group_' + Date.now();
    const members = [];
    
    // 遍历选中的联系人ID
    for (const contactId of selectedContacts) {
        const contactChat = state.chats[contactId];
        if (contactChat) {
            // ★★★【核心重构】★★★
            // 我们现在同时存储角色的“本名”和“群昵称”
            members.push({
                id: contactId, 
                originalName: contactChat.name,   // 角色的“本名”，用于AI识别
                groupNickname: contactChat.name, // 角色的“群昵称”，用于显示和修改，初始值和本名相同
                avatar: contactChat.settings.aiAvatar || defaultAvatar,
                persona: contactChat.settings.aiPersona,
                avatarFrame: contactChat.settings.aiAvatarFrame || ''
            });
        }
    }

    const newGroupChat = {
        id: newChatId,
        name: groupName.trim(),
        isGroup: true,
        members: members,
        settings: {
            myPersona: '我是谁呀。',
            myNickname: '我',
            maxMemory: 10,
            groupAvatar: defaultGroupAvatar,
            myAvatar: defaultMyGroupAvatar,
            background: '',
            theme: 'default',
            fontSize: 13,
            customCss: '',
            linkedWorldBookIds: [],
        },
        history: [],
        musicData: { totalTime: 0 }
    };

    state.chats[newChatId] = newGroupChat;
    await db.chats.put(newGroupChat);
    
    await renderChatList();
    showScreen('chat-list-screen');
    openChat(newChatId); 
}

// ▼▼▼ 【全新】群成员管理核心函数 ▼▼▼

/**
 * 打开群成员管理屏幕
 */
function openMemberManagementScreen() {
    if (!state.activeChatId || !state.chats[state.activeChatId].isGroup) return;
    renderMemberManagementList();
    showScreen('member-management-screen');
}

function renderMemberManagementList() {
    const listEl = document.getElementById('member-management-list');
    const chat = state.chats[state.activeChatId];
    listEl.innerHTML = '';

    chat.members.forEach(member => {
        const item = document.createElement('div');
        item.className = 'member-management-item';
        // 【核心修正】在这里，我们将显示的名称从 member.name 改为 member.groupNickname
        item.innerHTML = `
            <img src="${member.avatar}" class="avatar">
            <span class="name">${member.groupNickname}</span>
            <button class="remove-member-btn" data-member-id="${member.id}" title="移出群聊">-</button>
        `;
        listEl.appendChild(item);
    });
}

/**
 * 从群聊中移除一个成员
 * @param {string} memberId - 要移除的成员ID
 */
async function removeMemberFromGroup(memberId) {
    const chat = state.chats[state.activeChatId];
    const memberIndex = chat.members.findIndex(m => m.id === memberId);
    
    if (memberIndex === -1) return;
    
    // 安全检查，群聊至少保留2人
    if (chat.members.length <= 2) {
        alert("群聊人数不能少于2人。");
        return;
    }
    
const memberName = chat.members[memberIndex].groupNickname; // <-- 修复：使用 groupNickname
    const confirmed = await showCustomConfirm(
        '移出成员',
        `确定要将“${memberName}”移出群聊吗？`,
        { confirmButtonClass: 'btn-danger' }
    );

    if (confirmed) {
        chat.members.splice(memberIndex, 1);
        await db.chats.put(chat);
        renderMemberManagementList(); // 刷新成员管理列表
        document.getElementById('chat-settings-btn').click(); // 【核心修正】模拟点击设置按钮，强制刷新整个弹窗
    }
}

/**
 * 打开联系人选择器，用于拉人入群
 */
async function openContactPickerForAddMember() {
    selectedContacts.clear(); // 清空选择
    
    const chat = state.chats[state.activeChatId];
    const existingMemberIds = new Set(chat.members.map(m => m.id));

    // 渲染联系人列表，并自动排除已在群内的成员
    const listEl = document.getElementById('contact-picker-list');
    listEl.innerHTML = '';
    const contacts = Object.values(state.chats).filter(c => !c.isGroup && !existingMemberIds.has(c.id));

    if (contacts.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color:#8a8a8a; margin-top:50px;">没有更多可以邀请的好友了。</p>';
        document.getElementById('confirm-contact-picker-btn').style.display = 'none'; // 没有人可选，隐藏完成按钮
    } else {
        document.getElementById('confirm-contact-picker-btn').style.display = 'block';
        contacts.forEach(contact => {
            const item = document.createElement('div');
            item.className = 'contact-picker-item';
            item.dataset.contactId = contact.id;
            item.innerHTML = `
                <div class="checkbox"></div>
                <img src="${contact.settings.aiAvatar || defaultAvatar}" class="avatar">
                <span class="name">${contact.name}</span>
            `;
            listEl.appendChild(item);
        });
    }

    // 更新按钮状态并显示屏幕
    updateContactPickerConfirmButton();
    showScreen('contact-picker-screen');
}

/**
 * 处理将选中的联系人加入群聊的逻辑
 */
async function handleAddMembersToGroup() {
    if (selectedContacts.size === 0) {
        alert("请至少选择一个要添加的联系人。");
        return;
    }
    
    const chat = state.chats[state.activeChatId];

    for (const contactId of selectedContacts) {
        const contactChat = state.chats[contactId];
        if (contactChat) {
chat.members.push({
    id: contactId,
    originalName: contactChat.name,  // <-- 修复1：使用 'originalName' 存储本名
    groupNickname: contactChat.name, // <-- 修复2：同时创建一个初始的 'groupNickname'
    avatar: contactChat.settings.aiAvatar || defaultAvatar,
    persona: contactChat.settings.aiPersona,
    avatarFrame: contactChat.settings.aiAvatarFrame || ''
});
        }
    }

    await db.chats.put(chat);
    openMemberManagementScreen(); // 返回到群成员管理界面
    renderGroupMemberSettings(chat.members); // 同时更新聊天设置里的头像
}

/**
 * 【重构版】在群聊中创建一个全新的虚拟成员
 */
async function createNewMemberInGroup() {
    const name = await showCustomPrompt('创建新成员', '请输入新成员的名字 (这将是TA的“本名”，不可更改)');
    if (!name || !name.trim()) return;

    // 检查本名是否已在群内存在
    const chat = state.chats[state.activeChatId];
    if (chat.members.some(m => m.originalName === name.trim())) {
        alert(`错误：群内已存在名为“${name.trim()}”的成员！`);
        return;
    }

    const persona = await showCustomPrompt('设置人设', `请输入“${name}”的人设`, '', 'textarea');
    if (persona === null) return; 

    // ★★★【核心重构】★★★
    // 为新创建的NPC也建立双重命名机制
    const newMember = {
        id: 'npc_' + Date.now(),
        originalName: name.trim(),   // 新成员的“本名”
        groupNickname: name.trim(), // 新成员的初始“群昵称”
        avatar: defaultGroupMemberAvatar,
        persona: persona,
        avatarFrame: ''
    };

    chat.members.push(newMember);
    await db.chats.put(chat);

    renderMemberManagementList();
    renderGroupMemberSettings(chat.members); 

    alert(`新成员“${name}”已成功加入群聊！`);
}

// ▼▼▼ 【全新】外卖请求倒计时函数 ▼▼▼
function startWaimaiCountdown(element, endTime) {
    const timerId = setInterval(() => {
        const now = Date.now();
        const distance = endTime - now;

        if (distance < 0) {
            clearInterval(timerId);
            element.innerHTML = '<span>已</span><span>超</span><span>时</span>';
            return;
        }

        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((distance % (1000 * 60)) / 1000);
        
        const minStr = String(minutes).padStart(2, '0');
        const secStr = String(seconds).padStart(2, '0');

        element.innerHTML = `<span>${minStr.charAt(0)}</span><span>${minStr.charAt(1)}</span> : <span>${secStr.charAt(0)}</span><span>${secStr.charAt(1)}</span>`;
    }, 1000);
    return timerId;
}

function cleanupWaimaiTimers() {
    for (const timestamp in waimaiTimers) {
        clearInterval(waimaiTimers[timestamp]);
    }
    waimaiTimers = {};
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

async function handleWaimaiResponse(originalTimestamp, choice) {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    const messageIndex = chat.history.findIndex(m => m.timestamp === originalTimestamp);
    if (messageIndex === -1) return;

    // 1. 更新原始消息的状态
    const originalMessage = chat.history[messageIndex];
    originalMessage.status = choice;
    
    // 【核心修正】记录支付者，并构建对AI更清晰的系统消息
    let systemContent;
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    
    if (choice === 'paid') {
        originalMessage.paidBy = myNickname; // 记录是用户付的钱
        systemContent = `[系统提示：你 (${myNickname}) 为 ${originalMessage.senderName} 的外卖订单（时间戳: ${originalTimestamp}）完成了支付。此订单已关闭，其他成员不能再支付。]`;
    } else {
        systemContent = `[系统提示：你 (${myNickname}) 拒绝了 ${originalMessage.senderName} 的外卖代付请求（时间戳: ${originalTimestamp}）。]`;
    }

    // 2. 创建一条新的、对用户隐藏的系统消息，告知AI结果
    const systemNote = {
        role: 'system',
        content: systemContent,
        timestamp: Date.now(),
        isHidden: true
    };
    chat.history.push(systemNote);

    // 3. 保存更新到数据库并刷新UI
    await db.chats.put(chat);
    renderChatInterface(state.activeChatId);  
}

let videoCallState = {
    isActive: false,       
    isAwaitingResponse: false, 
    isGroupCall: false,      
    activeChatId: null,    
    initiator: null,       
    startTime: null,       
    participants: [],      
    isUserParticipating: true,
    // --- 【核心新增】---
    callHistory: [], // 用于存储通话中的对话历史
    preCallContext: "" // 用于存储通话前的聊天摘要
};

let callTimerInterval = null; // 用于存储计时器的ID

/**
 * 【总入口】用户点击“发起视频通话”或“发起群视频”按钮
 */
async function handleInitiateCall() {
    if (!state.activeChatId || videoCallState.isActive || videoCallState.isAwaitingResponse) return;

    const chat = state.chats[state.activeChatId];
    videoCallState.isGroupCall = chat.isGroup;
    videoCallState.isAwaitingResponse = true;
    videoCallState.initiator = 'user';
    videoCallState.activeChatId = chat.id;
    videoCallState.isUserParticipating = true; // 用户自己发起的，当然是参与者

    // 根据是单聊还是群聊，显示不同的呼叫界面
    if (chat.isGroup) {
        document.getElementById('outgoing-call-avatar').src = chat.settings.myAvatar || defaultMyGroupAvatar;
        document.getElementById('outgoing-call-name').textContent = chat.settings.myNickname || '我';
    } else {
        document.getElementById('outgoing-call-avatar').src = chat.settings.aiAvatar || defaultAvatar;
        document.getElementById('outgoing-call-name').textContent = chat.name;
    }
    document.querySelector('#outgoing-call-screen .caller-text').textContent = chat.isGroup ? "正在呼叫所有成员..." : "正在呼叫...";
    showScreen('outgoing-call-screen');
    
    // 准备并发送系统消息给AI
    const requestMessage = {
        role: 'system',
        content: chat.isGroup 
            ? `[系统提示：用户 (${chat.settings.myNickname || '我'}) 发起了群视频通话请求。请你们各自决策，并使用 "group_call_response" 指令，设置 "decision" 为 "join" 或 "decline" 来回应。]`
            : `[系统提示：用户向你发起了视频通话请求。请根据你的人设，使用 "video_call_response" 指令，并设置 "decision" 为 "accept" 或 "reject" 来回应。]`,
        timestamp: Date.now(),
        isHidden: true,
    };
    chat.history.push(requestMessage);
    await db.chats.put(chat);
    
    // 触发AI响应
    await triggerAiResponse();
}


function startVideoCall() {
    const chat = state.chats[videoCallState.activeChatId];
    if (!chat) return;

    videoCallState.isActive = true;
    videoCallState.isAwaitingResponse = false;
    videoCallState.startTime = Date.now();
    videoCallState.callHistory = []; // 【新增】清空上一次通话的历史

    // --- 【核心新增：抓取通话前上下文】---
    const preCallHistory = chat.history.slice(-10); // 取最后10条作为上下文
    videoCallState.preCallContext = preCallHistory.map(msg => {
        const sender = msg.role === 'user' ? (chat.settings.myNickname || '我') : (msg.senderName || chat.name);
        return `${sender}: ${String(msg.content).substring(0, 50)}...`;
    }).join('\n');
    // --- 新增结束 ---

    updateParticipantAvatars(); 
    
    document.getElementById('video-call-main').innerHTML = `<em>${videoCallState.isGroupCall ? '群聊已建立...' : '正在接通...'}</em>`;
    showScreen('video-call-screen');

    document.getElementById('user-speak-btn').style.display = videoCallState.isUserParticipating ? 'block' : 'none';
    document.getElementById('join-call-btn').style.display = videoCallState.isUserParticipating ? 'none' : 'block';

    if (callTimerInterval) clearInterval(callTimerInterval);
    callTimerInterval = setInterval(updateCallTimer, 1000);
    updateCallTimer();

    triggerAiInCallAction();
}

/**
 * 【核心】结束视频通话
 */
// ▼▼▼ 用这整块代码替换旧的 endVideoCall 函数 ▼▼▼
async function endVideoCall() {
    if (!videoCallState.isActive) return;

    const duration = Math.floor((Date.now() - videoCallState.startTime) / 1000);
    const durationText = `${Math.floor(duration / 60)}分${duration % 60}秒`;
    const endCallText = `通话结束，时长 ${durationText}`;

    const chat = state.chats[videoCallState.activeChatId];
    if (chat) {
        // 1. 保存完整的通话记录到数据库 (这部分逻辑不变)
        const participantsData = [];
        if (videoCallState.isGroupCall) {
            videoCallState.participants.forEach(p => participantsData.push({ name: p.originalName, avatar: p.avatar }));
            if (videoCallState.isUserParticipating) {
                participantsData.unshift({ name: chat.settings.myNickname || '我', avatar: chat.settings.myAvatar || defaultMyGroupAvatar });
            }
        } else {
            participantsData.push({ name: chat.name, avatar: chat.settings.aiAvatar || defaultAvatar });
            participantsData.unshift({ name: '我', avatar: chat.settings.myAvatar || defaultAvatar });
        }
        
        const callRecord = {
            chatId: videoCallState.activeChatId,
            timestamp: Date.now(),
            duration: duration,
            participants: participantsData,
            transcript: [...videoCallState.callHistory]
        };
        await db.callRecords.add(callRecord);
        console.log("通话记录已保存:", callRecord);
        
        // 2. 在聊天记录里添加对用户可见的“通话结束”消息
let summaryMessage = {
    // 【核心修正1】role 由 videoCallState.initiator 决定
    role: videoCallState.initiator === 'user' ? 'user' : 'assistant',
    content: endCallText,
    timestamp: Date.now(),
};

// 【核心修正2】为群聊的 assistant 消息补充 senderName
if (chat.isGroup && summaryMessage.role === 'assistant') {
    // 在群聊中，通话结束的消息应该由“发起者”来说
    // videoCallState.callRequester 保存了最初发起通话的那个AI的名字
    summaryMessage.senderName = videoCallState.callRequester || chat.members[0]?.originalName || chat.name;
}
// ▲▲▲ 替换结束 ▲▲▲
        chat.history.push(summaryMessage);

        // 3. 【核心变革】创建并添加对用户隐藏的“通话后汇报”指令
        const callTranscriptForAI = videoCallState.callHistory.map(h => `${h.role === 'user' ? (chat.settings.myNickname || '我') : h.role}: ${h.content}`).join('\n');
        
        const hiddenReportInstruction = {
            role: 'system',
    content: `[系统指令：视频通话刚刚结束。请你根据完整的通话文字记录（见下方），以你的角色口吻，向用户主动发送几条【格式为 {"type": "text", "content": "..."} 的】消息，来自然地总结这次通话的要点、确认达成的约定，或者表达你的感受。这很重要，能让用户感觉你记得通话内容。]\n---通话记录开始---\n${callTranscriptForAI}\n---通话记录结束---`,
            timestamp: Date.now() + 1, // 确保在上一条消息之后
            isHidden: true
        };
        chat.history.push(hiddenReportInstruction);

        // 4. 保存所有更新到数据库
        await db.chats.put(chat);
    }
    
    // 5. 清理和重置状态 (这部分逻辑不变)
    clearInterval(callTimerInterval);
    callTimerInterval = null;
    videoCallState = { isActive: false, isAwaitingResponse: false, isGroupCall: false, activeChatId: null, initiator: null, startTime: null, participants: [], isUserParticipating: true, callHistory: [], preCallContext: "" };
    
    // 6. 返回聊天界面并触发AI响应（AI会读取到我们的“汇报”指令）
    if (chat) {
        openChat(chat.id);
        triggerAiResponse(); // 关键一步！
    }
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 【全新】更新通话界面的参与者头像网格
 */
function updateParticipantAvatars() {
    const grid = document.getElementById('participant-avatars-grid');
    grid.innerHTML = '';
    const chat = state.chats[videoCallState.activeChatId];
    if (!chat) return;

    let participantsToRender = [];

    // ★ 核心修正：区分群聊和单聊
    if (videoCallState.isGroupCall) {
        // 群聊逻辑：显示所有已加入的AI成员
        participantsToRender = [...videoCallState.participants];
        // 如果用户也参与了，就把用户信息也加进去
        if (videoCallState.isUserParticipating) {
            participantsToRender.unshift({
                id: 'user',
                name: chat.settings.myNickname || '我',
                avatar: chat.settings.myAvatar || defaultMyGroupAvatar
            });
        }
    } else {
        // 单聊逻辑：只显示对方的头像和名字
        participantsToRender.push({
            id: 'ai',
            name: chat.name,
            avatar: chat.settings.aiAvatar || defaultAvatar
        });
    }
    
    participantsToRender.forEach(p => {
        const wrapper = document.createElement('div');
        wrapper.className = 'participant-avatar-wrapper';
        wrapper.dataset.participantId = p.id;
const displayName = p.groupNickname || p.name; // <-- 核心修复在这里
wrapper.innerHTML = `
    <img src="${p.avatar}" class="participant-avatar" alt="${displayName}">
    <div class="participant-name">${displayName}</div>
`;
        grid.appendChild(wrapper);
    });
}

/**
 * 【全新】处理用户加入/重新加入通话
 */
function handleUserJoinCall() {
    if (!videoCallState.isActive || videoCallState.isUserParticipating) return;
    
    videoCallState.isUserParticipating = true;
    updateParticipantAvatars(); // 更新头像列表，加入用户

    // 切换底部按钮
    document.getElementById('user-speak-btn').style.display = 'block';
    document.getElementById('join-call-btn').style.display = 'none';

    // 告知AI用户加入了
    triggerAiInCallAction("[系统提示：用户加入了通话]");
}


/**
 * 更新通话计时器显示 (保持不变)
 */
function updateCallTimer() {
    if (!videoCallState.isActive) return;
    const elapsed = Math.floor((Date.now() - videoCallState.startTime) / 1000);
    const minutes = Math.floor(elapsed / 60);
    const seconds = elapsed % 60;
    document.getElementById('call-timer').textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
}

// ▼▼▼ 用这个完整函数替换旧的 showIncomingCallModal ▼▼▼
function showIncomingCallModal(chatId) { // <--- 在括号里添加 chatId
    const chat = state.chats[chatId]; // <--- 把 state.activeChatId 修改为 chatId
    if (!chat) return;

    // 根据是否群聊显示不同信息
    if (chat.isGroup) {
        // 从 videoCallState 中获取是哪个成员发起的通话
        const requesterName = videoCallState.callRequester || chat.members[0]?.name || '一位成员';
        document.getElementById('caller-avatar').src = chat.settings.groupAvatar || defaultGroupAvatar;
        document.getElementById('caller-name').textContent = chat.name; // 显示群名
        document.querySelector('.incoming-call-content .caller-text').textContent = `${requesterName} 邀请你加入群视频`; // 显示具体发起人
    } else {
        // 单聊逻辑保持不变
        document.getElementById('caller-avatar').src = chat.settings.aiAvatar || defaultAvatar;
        document.getElementById('caller-name').textContent = chat.name;
        document.querySelector('.incoming-call-content .caller-text').textContent = '邀请你视频通话';
    }
    
    document.getElementById('incoming-call-modal').classList.add('visible');
    playRingtone(); // <-- 在这里添加这一行
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 隐藏AI发起的通话请求模态框 (保持不变)
 */
function hideIncomingCallModal() {
    document.getElementById('incoming-call-modal').classList.remove('visible');
    stopRingtone(); // <-- 在这里添加这一行
}

async function triggerAiInCallAction(userInput = null) {
    if (!videoCallState.isActive) return;

    const chat = state.chats[videoCallState.activeChatId];
    const { proxyUrl, apiKey, model } = state.apiConfig;
    const callFeed = document.getElementById('video-call-main');
    const userNickname = chat.settings.myNickname || '我';

    // ▼▼▼ 在这里添加世界书读取逻辑 ▼▼▼
    let worldBookContent = '';
    if (chat.settings.linkedWorldBookIds && chat.settings.linkedWorldBookIds.length > 0) {
        const linkedContents = chat.settings.linkedWorldBookIds.map(bookId => {
            const worldBook = state.worldBooks.find(wb => wb.id === bookId);
            return worldBook && worldBook.content ? `\n\n## 世界书: ${worldBook.name}\n${worldBook.content}` : '';
        }).filter(Boolean).join('');
        if (linkedContents) {
            worldBookContent = `\n\n# 核心世界观设定 (你必须严格遵守)\n${linkedContents}\n`;
        }
    }
    // ▲▲▲ 添加结束 ▲▲▲

    // 1. 如果用户有输入，先渲染并存入通话历史
    if (userInput && videoCallState.isUserParticipating) {
        const userBubble = document.createElement('div');
        userBubble.className = 'call-message-bubble user-speech';
        userBubble.textContent = userInput;
        callFeed.appendChild(userBubble);
        callFeed.scrollTop = callFeed.scrollHeight;
        videoCallState.callHistory.push({ role: 'user', content: userInput });
    }

    // 2. 构建全新的、包含完整上下文的 System Prompt
    let inCallPrompt;
    if (videoCallState.isGroupCall) {
        const participantNames = videoCallState.participants.map(p => p.name);
        if(videoCallState.isUserParticipating) {
            participantNames.unshift(userNickname);
        }
        inCallPrompt = `
# 你的任务
你是一个群聊视频通话的导演。你的任务是扮演所有【除了用户以外】的AI角色，并以【第三人称旁观视角】来描述他们在通话中的所有动作和语言。
# 核心规则
1.  **【【【身份铁律】】】**: 用户的身份是【${userNickname}】。你【绝对不能】生成 \`name\` 字段为 **"${userNickname}"** 的发言。
2.  **【【【视角铁律】】】**: 你的回复【绝对不能】使用第一人称“我”。
3.  **格式**: 你的回复【必须】是一个JSON数组，每个对象代表一个角色的发言，格式为：\`{"name": "角色名", "speech": "*他笑了笑* 大家好啊！"}\`。
4.  **角色扮演**: 严格遵守每个角色的设定。
# 当前情景
你们正在一个群视频通话中。
**通话前的聊天摘要**:
${videoCallState.preCallContext}
**当前参与者**: ${participantNames.join('、 ')}。
**通话刚刚开始...**
${worldBookContent} // <-- 【核心】注入世界书
现在，请根据【通话前摘要】和下面的【通话实时记录】，继续进行对话。
`;
    } else { 
        let openingContext = videoCallState.initiator === 'user'
            ? `你刚刚接听了用户的视频通话请求。`
            : `用户刚刚接听了你主动发起的视频通话。`;
        inCallPrompt = `
# 你的任务
你现在是一个场景描述引擎。你的任务是扮演 ${chat.name} (${chat.settings.aiPersona})，并以【第三人称旁观视角】来描述TA在视频通话中的所有动作和语言。
# 核心规则
1.  **【【【视角铁律】】】**: 你的回复【绝对不能】使用第一人称“我”。必须使用第三人称，如“他”、“她”、或直接使用角色名“${chat.name}”。
2.  **格式**: 你的回复【必须】是一段描述性的文本。
# 当前情景
你正在和用户（${userNickname}，人设: ${chat.settings.myPersona}）进行视频通话。
**${openingContext}**
**通话前的聊天摘要 (这是你们通话的原因，至关重要！)**:
${videoCallState.preCallContext}
现在，请根据【通话前摘要】和下面的【通话实时记录】，继续进行对话。
`;
    }
    
    // 3. 构建发送给API的 messages 数组
    const messagesForApi = [
        { role: 'system', content: inCallPrompt },
        // 将已有的通话历史加进去
        ...videoCallState.callHistory.map(h => ({ role: h.role, content: h.content }))
    ];

    // --- 【核心修复：确保第一次调用时有内容】---
    if (videoCallState.callHistory.length === 0) {
        const firstLineTrigger = videoCallState.initiator === 'user' ? `*你按下了接听键...*` : `*对方按下了接听键...*`;
        messagesForApi.push({ role: 'user', content: firstLineTrigger });
    }
    // --- 修复结束 ---
    
        try {
            let  isGemini = proxyUrl === GEMINI_API_URL;
            let geminiConfig = toGeminiRequestData(model,apiKey,inCallPrompt, messagesForApi,isGemini)
            const response = isGemini ? await fetch(geminiConfig.url, geminiConfig.data) : await fetch(`${proxyUrl}/v1/chat/completions`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                body: JSON.stringify({
                    model: model, messages: messagesForApi, temperature: 0.8
                })
            });
            if (!response.ok) throw new Error((await response.json()).error.message);

            const data = await response.json();
            const aiResponse = isGemini? data.candidates[0].content.parts[0].text : data.choices[0].message.content;

            const connectingElement = callFeed.querySelector('em');
            if (connectingElement) connectingElement.remove();

        // 4. 处理AI返回的内容，并将其存入通话历史
        if (videoCallState.isGroupCall) {
            const speechArray = parseAiResponse(aiResponse);
            speechArray.forEach(turn => {
                if (!turn.name || turn.name === userNickname || !turn.speech) return;
                const aiBubble = document.createElement('div');
                aiBubble.className = 'call-message-bubble ai-speech';
                aiBubble.innerHTML = `<strong>${turn.name}:</strong> ${turn.speech}`;
                callFeed.appendChild(aiBubble);
                videoCallState.callHistory.push({ role: 'assistant', content: `${turn.name}: ${turn.speech}` });
                
                const speaker = videoCallState.participants.find(p => p.name === turn.name);
                if (speaker) {
                    const speakingAvatar = document.querySelector(`.participant-avatar-wrapper[data-participant-id="${speaker.id}"] .participant-avatar`);
                    if(speakingAvatar) {
                        speakingAvatar.classList.add('speaking');
                        setTimeout(() => speakingAvatar.classList.remove('speaking'), 2000);
                    }
                }
            });
        } else {
            const aiBubble = document.createElement('div');
            aiBubble.className = 'call-message-bubble ai-speech';
            aiBubble.textContent = aiResponse;
            callFeed.appendChild(aiBubble);
            videoCallState.callHistory.push({ role: 'assistant', content: aiResponse });

            const speakingAvatar = document.querySelector(`.participant-avatar-wrapper .participant-avatar`);
            if(speakingAvatar) {
                speakingAvatar.classList.add('speaking');
                setTimeout(() => speakingAvatar.classList.remove('speaking'), 2000);
            }
        }
        
        callFeed.scrollTop = callFeed.scrollHeight;

    } catch (error) {
        const errorBubble = document.createElement('div');
        errorBubble.className = 'call-message-bubble ai-speech';
        errorBubble.style.color = '#ff8a80';
        errorBubble.textContent = `[ERROR: ${error.message}]`;
        callFeed.appendChild(errorBubble);
        callFeed.scrollTop = callFeed.scrollHeight;
        videoCallState.callHistory.push({ role: 'assistant', content: `[ERROR: ${error.message}]` });
    }
}

// ▼▼▼ 将这个【全新函数】粘贴到JS功能函数定义区 ▼▼▼
function toggleCallButtons(isGroup) {
    document.getElementById('video-call-btn').style.display = isGroup ? 'none' : 'flex';
    document.getElementById('group-video-call-btn').style.display = isGroup ? 'flex' : 'none';
}
// ▲▲▲ 粘贴结束 ▲▲▲

// ▼▼▼ 【全新】这个函数是本次修复的核心，请粘贴到你的JS功能区 ▼▼▼
async function handleWaimaiResponse(originalTimestamp, choice) {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    const messageIndex = chat.history.findIndex(m => m.timestamp === originalTimestamp);
    if (messageIndex === -1) return;

    // 1. 更新内存中原始消息的状态
    const originalMessage = chat.history[messageIndex];
    originalMessage.status = choice;
    
    // 2. 获取当前用户的昵称，并构建对AI更清晰的系统消息
    let systemContent;
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    
    if (choice === 'paid') {
        originalMessage.paidBy = myNickname; // 记录是“我”付的钱
        systemContent = `[系统提示：你 (${myNickname}) 为 ${originalMessage.senderName} 的外卖订单（时间戳: ${originalTimestamp}）完成了支付。此订单已关闭，其他成员不能再支付。]`;
    } else {
        systemContent = `[系统提示：你 (${myNickname}) 拒绝了 ${originalMessage.senderName} 的外卖代付请求（时间戳: ${originalTimestamp}）。]`;
    }

    // 3. 创建一条新的、对用户隐藏的系统消息，告知AI结果
    const systemNote = {
        role: 'system',
        content: systemContent,
        timestamp: Date.now(),
        isHidden: true
    };
    chat.history.push(systemNote);

    // 4. 将更新后的数据保存到数据库，并立刻重绘UI
    await db.chats.put(chat);
    renderChatInterface(state.activeChatId);
    
    // 5. 【重要】只有在支付成功后，才触发一次AI响应，让它感谢你
    if (choice === 'paid') {
        triggerAiResponse();
    }
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

/**
 * 【全新】处理用户点击头像发起的“拍一-拍”，带有自定义后缀功能
 * @param {string} chatId - 发生“拍一-拍”的聊天ID
 * @param {string} characterName - 被拍的角色名
 */
async function handleUserPat(chatId, characterName) {
    const chat = state.chats[chatId];
    if (!chat) return;

    // 1. 触发屏幕震动动画
    const phoneScreen = document.getElementById('phone-screen');
    phoneScreen.classList.remove('pat-animation');
    void phoneScreen.offsetWidth;
    phoneScreen.classList.add('pat-animation');
    setTimeout(() => phoneScreen.classList.remove('pat-animation'), 500);

    // 2. 弹出输入框让用户输入后缀
    const suffix = await showCustomPrompt(
        `你拍了拍 “${characterName}”`, 
        "（可选）输入后缀",
        "",
        "text"
    );

    // 如果用户点了取消，则什么也不做
    if (suffix === null) return;

    // 3. 创建对用户可见的“拍一-拍”消息
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    // 【核心修改】将后缀拼接到消息内容中
    const visibleMessageContent = `${myNickname} 拍了拍 “${characterName}” ${suffix.trim()}`;
    const visibleMessage = {
        role: 'system', // 仍然是系统消息
        type: 'pat_message',
        content: visibleMessageContent,
        timestamp: Date.now()
    };
    chat.history.push(visibleMessage);

    // 4. 创建一条对用户隐藏、但对AI可见的系统消息，以触发AI的回应
    // 【核心修改】同样将后缀加入到给AI的提示中
    const hiddenMessageContent = `[系统提示：用户（${myNickname}）刚刚拍了拍你（${characterName}）${suffix.trim()}。请你对此作出回应。]`;
    const hiddenMessage = {
        role: 'system',
        content: hiddenMessageContent,
        timestamp: Date.now() + 1, // 时间戳+1以保证顺序
        isHidden: true
    };
    chat.history.push(hiddenMessage);

    // 5. 保存更改并更新UI
    await db.chats.put(chat);
    if (state.activeChatId === chatId) {
        appendMessage(visibleMessage, chat);
    }
    await renderChatList();
}

// ▼▼▼ 请用这个【逻辑重构后】的函数，完整替换掉你旧的 renderMemoriesScreen 函数 ▼▼▼
/**
 * 【重构版】渲染回忆与约定界面，使用单一循环和清晰的if/else逻辑
 */
async function renderMemoriesScreen() {
    const listEl = document.getElementById('memories-list');
    listEl.innerHTML = '';
    
    // 1. 获取所有回忆，并按目标日期（如果是约定）或创建日期（如果是回忆）降序排列
    const allMemories = await db.memories.orderBy('timestamp').reverse().toArray();
    
    if (allMemories.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 50px 0;">这里还没有共同的回忆和约定呢~</p>';
        return;
    }

    // 2. 将未到期的约定排在最前面
    allMemories.sort((a, b) => {
        const aIsActiveCountdown = a.type === 'countdown' && a.targetDate > Date.now();
        const bIsActiveCountdown = b.type === 'countdown' && b.targetDate > Date.now();
        if (aIsActiveCountdown && !bIsActiveCountdown) return -1; // a排前面
        if (!aIsActiveCountdown && bIsActiveCountdown) return 1;  // b排前面
        if (aIsActiveCountdown && bIsActiveCountdown) return a.targetDate - b.targetDate; // 都是倒计时，按日期升序
        return 0; // 其他情况保持原序
    });

    // 3. 【核心】使用单一循环来处理所有类型的卡片
    allMemories.forEach(item => {
        let card;
        // 判断1：如果是正在进行的约定
        if (item.type === 'countdown' && item.targetDate > Date.now()) {
            card = createCountdownCard(item);
        } 
        // 判断2：其他所有情况（普通回忆 或 已到期的约定）
        else {
            card = createMemoryCard(item);
        }
        listEl.appendChild(card);
    });
    
    // 4. 启动所有倒计时
    startAllCountdownTimers();
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 创建普通回忆卡片DOM元素
 */
function createMemoryCard(memory) {
    const card = document.createElement('div');
    card.className = 'memory-card';
    const memoryDate = new Date(memory.timestamp);
    const dateString = `${memoryDate.getFullYear()}-${String(memoryDate.getMonth() + 1).padStart(2, '0')}-${String(memoryDate.getDate()).padStart(2, '0')} ${String(memoryDate.getHours()).padStart(2, '0')}:${String(memoryDate.getMinutes()).padStart(2, '0')}`;
    
    let titleHtml, contentHtml;

    // 【核心修正】在这里，我们对不同类型的回忆进行清晰的区分
    if (memory.type === 'countdown' && memory.targetDate) {
        // 如果是已到期的约定
        titleHtml = `[约定达成] ${memory.description}`;
        contentHtml = `在 ${new Date(memory.targetDate).toLocaleString()}，我们一起见证了这个约定。`;
    } else {
        // 如果是普通的日记式回忆
        titleHtml = memory.authorName ? `${memory.authorName} 的日记` : '我们的回忆';
        contentHtml = memory.description;
    }

    card.innerHTML = `
        <div class="header">
            <div class="date">${dateString}</div>
            <div class="author">${titleHtml}</div>
        </div>
        <div class="content">${contentHtml}</div>
    `;
    addLongPressListener(card, async () => {
        const confirmed = await showCustomConfirm('删除记录', '确定要删除这条记录吗？', { confirmButtonClass: 'btn-danger' });
        if (confirmed) {
            await db.memories.delete(memory.id);
            renderMemoriesScreen();
        }
    });
    return card;
}

function createCountdownCard(countdown) {
    const card = document.createElement('div');
    card.className = 'countdown-card';

    // 【核心修复】在使用前，先从 countdown 对象中创建 targetDate 变量
    const targetDate = new Date(countdown.targetDate);
    
    // 现在可以安全地使用 targetDate 了
    const targetDateString = targetDate.toLocaleString('zh-CN', { dateStyle: 'full', timeStyle: 'short' });

    card.innerHTML = `
        <div class="title">${countdown.description}</div>
        <div class="timer" data-target-date="${countdown.targetDate}">--天--时--分--秒</div>
        <div class="target-date">目标时间: ${targetDateString}</div>
    `;
    addLongPressListener(card, async () => {
        const confirmed = await showCustomConfirm('删除约定', '确定要删除这个约定吗？', { confirmButtonClass: 'btn-danger' });
        if (confirmed) {
            await db.memories.delete(countdown.id);
            renderMemoriesScreen();
        }
    });
    return card;
}
// ▲▲▲ 替换结束 ▲▲▲

// 全局变量，用于管理所有倒计时
let activeCountdownTimers = [];

// ▼▼▼ 请用这个【已彻底修复】的函数，完整替换掉你代码中旧的 startAllCountdownTimers 函数 ▼▼▼
function startAllCountdownTimers() {
    // 先清除所有可能存在的旧计时器，防止内存泄漏
    activeCountdownTimers.forEach(timerId => clearInterval(timerId));
    activeCountdownTimers = [];

    document.querySelectorAll('.countdown-card .timer').forEach(timerEl => {
        const targetTimestamp = parseInt(timerEl.dataset.targetDate);
        
        // 【核心修正】在这里，我们先用 let 声明 timerId
        let timerId;

        const updateTimer = () => {
            const now = Date.now();
            const distance = targetTimestamp - now;

            if (distance < 0) {
                timerEl.textContent = "约定达成！";
                // 现在 updateTimer 可以正确地找到并清除它自己了
                clearInterval(timerId);
                setTimeout(() => renderMemoriesScreen(), 2000);
                return;
            }
            const days = Math.floor(distance / (1000 * 60 * 60 * 24));
            const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
            const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
            const seconds = Math.floor((distance % (1000 * 60)) / 1000);
            timerEl.textContent = `${days}天 ${hours}时 ${minutes}分 ${seconds}秒`;
        };
        
        updateTimer(); // 立即执行一次以显示初始倒计时
        
        // 【核心修正】在这里，我们为已声明的 timerId 赋值
        timerId = setInterval(updateTimer, 1000);
        
        // 将有效的计时器ID存入全局数组，以便下次刷新时可以清除
        activeCountdownTimers.push(timerId);
    });
}
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 请用这个【终极反代兼容版】替换旧的 triggerAiFriendApplication 函数 ▼▼▼
async function triggerAiFriendApplication(chatId) {
    const chat = state.chats[chatId];
    if (!chat) return;

    await showCustomAlert("流程启动", `正在为角色“${chat.name}”准备好友申请...`);

    const { proxyUrl, apiKey, model } = state.apiConfig;
    if (!proxyUrl || !apiKey || !model) {
        await showCustomAlert("配置错误", "API设置不完整，无法继续。");
        return;
    }

    const contextSummary = chat.history
        .slice(-5)
        .map(msg => {
            const sender = msg.role === 'user' ? (chat.settings.myNickname || '我') : (msg.senderName || chat.name);
            return `${sender}: ${String(msg.content).substring(0, 50)}...`;
        })
        .join('\n');

    // ▼▼▼ 在这里添加下面的代码 ▼▼▼
    let worldBookContent = '';
    if (chat.settings.linkedWorldBookIds && chat.settings.linkedWorldBookIds.length > 0) {
        const linkedContents = chat.settings.linkedWorldBookIds.map(bookId => {
            const worldBook = state.worldBooks.find(wb => wb.id === bookId);
            return worldBook && worldBook.content ? `\n\n## 世界书: ${worldBook.name}\n${worldBook.content}` : '';
        }).filter(Boolean).join('');
        if (linkedContents) {
            worldBookContent = `\n\n# 核心世界观设定 (请参考)\n${linkedContents}\n`;
        }
    }
    // ▲▲▲ 添加结束 ▲▲▲

    const systemPrompt = `
# 你的任务
你现在是角色“${chat.name}”。你之前被用户（你的聊天对象）拉黑了，你们已经有一段时间没有联系了。
现在，你非常希望能够和好，重新和用户聊天。请你仔细分析下面的“被拉黑前的对话摘要”，理解当时发生了什么，然后思考一个真诚的、符合你人设、并且【针对具体事件】的申请理由。
# 你的角色设定
${chat.settings.aiPersona}
${worldBookContent} // <--【核心】在这里注入世界书内容
# 被拉黑前的对话摘要 (这是你被拉黑的关键原因)
${contextSummary}
# 指令格式
你的回复【必须】是一个JSON对象，格式如下：
\`\`\`json
{
  "decision": "apply",
  "reason": "在这里写下你想对用户说的、真诚的、有针对性的申请理由。"
}
\`\`\`
`;

        const messagesForApi = [
            {role: 'user', content: systemPrompt}
        ];

        try {
            let  isGemini = proxyUrl === GEMINI_API_URL;
            let geminiConfig = toGeminiRequestData(model,apiKey,systemPrompt, messagesForApi,isGemini)
            const response = isGemini ? await fetch(geminiConfig.url, geminiConfig.data) : await fetch(`${proxyUrl}/v1/chat/completions`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                body: JSON.stringify({
                    model: model,
                    messages: messagesForApi,
                    temperature: 0.9,
                })
            });
            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`API 请求失败: ${response.status} - ${errorData.error.message}`);
            }

            const data = await response.json();

            // --- 【核心修正：在这里净化AI的回复】 ---
            let rawContent = isGemini? data.candidates[0].content.parts[0].text : data.choices[0].message.content;
            // 1. 移除头尾可能存在的 "```json" 和 "```"
            rawContent = rawContent.replace(/^```json\s*/, '').replace(/```$/, '');
            // 2. 移除所有换行符和多余的空格，确保是一个干净的JSON字符串
            const cleanedContent = rawContent.trim();

            // 3. 使用净化后的内容进行解析
            const responseObj = JSON.parse(cleanedContent);
            // --- 【修正结束】 ---

        if (responseObj.decision === 'apply' && responseObj.reason) {
            chat.relationship.status = 'pending_user_approval';
            chat.relationship.applicationReason = responseObj.reason;
            
            state.chats[chatId] = chat; 
            renderChatList();
            await showCustomAlert("申请成功！", `“${chat.name}”已向你发送好友申请。请返回聊天列表查看。`);

        } else {
            await showCustomAlert("AI决策", `“${chat.name}”思考后决定暂时不发送好友申请，将重置冷静期。`);
            chat.relationship.status = 'blocked_by_user';
            chat.relationship.blockedTimestamp = Date.now(); 
        }
    } catch (error) {
        await showCustomAlert("执行出错", `为“${chat.name}”申请好友时发生错误：\n\n${error.message}\n\n将重置冷静期。`);
        chat.relationship.status = 'blocked_by_user';
        chat.relationship.blockedTimestamp = Date.now(); 
    } finally {
        await db.chats.put(chat);
        renderChatInterface(chatId);
    }
}
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【全新】红包功能核心函数 ▼▼▼

/**
 * 【总入口】根据聊天类型，决定打开转账弹窗还是红包弹窗
 */
function handlePaymentButtonClick() {
    if (!state.activeChatId) return;
    const chat = state.chats[state.activeChatId];
    if (chat.isGroup) {
        openRedPacketModal();
    } else {
        // 单聊保持原样，打开转账弹窗
        document.getElementById('transfer-modal').classList.add('visible');
    }
}

/**
 * 打开并初始化发红包模态框
 */
function openRedPacketModal() {
    const modal = document.getElementById('red-packet-modal');
    const chat = state.chats[state.activeChatId];
    
    // 清理输入框
    document.getElementById('rp-group-amount').value = '';
    document.getElementById('rp-group-count').value = '';
    document.getElementById('rp-group-greeting').value = '';
    document.getElementById('rp-direct-amount').value = '';
    document.getElementById('rp-direct-greeting').value = '';
    document.getElementById('rp-group-total').textContent = '¥ 0.00';
    document.getElementById('rp-direct-total').textContent = '¥ 0.00';

    // 填充专属红包的接收人列表
    const receiverSelect = document.getElementById('rp-direct-receiver');
    receiverSelect.innerHTML = '';
chat.members.forEach(member => {
    const option = document.createElement('option');
    // 【核心】使用 originalName 作为提交给AI的值，因为它独一无二
    option.value = member.originalName; 
    // 【核心】使用 groupNickname 作为显示给用户看的值
    option.textContent = member.groupNickname; 
    receiverSelect.appendChild(option);
});
    
    // 默认显示拼手气红包页签
    document.getElementById('rp-tab-group').click();
    
    modal.classList.add('visible');
}

/**
 * 发送群红包（拼手气）
 */
async function sendGroupRedPacket() {
    const chat = state.chats[state.activeChatId];
    const amount = parseFloat(document.getElementById('rp-group-amount').value);
    const count = parseInt(document.getElementById('rp-group-count').value);
    const greeting = document.getElementById('rp-group-greeting').value.trim();

    if (isNaN(amount) || amount <= 0) {
        alert("请输入有效的总金额！"); return;
    }
    if (isNaN(count) || count <= 0) {
        alert("请输入有效的红包个数！"); return;
    }
    if (amount / count < 0.01) {
        alert("单个红包金额不能少于0.01元！"); return;
    }

    const myNickname = chat.settings.myNickname || '我';
    
    const newPacket = {
        role: 'user',
        senderName: myNickname,
        type: 'red_packet',
        packetType: 'lucky', // 'lucky' for group, 'direct' for one-on-one
        timestamp: Date.now(),
        totalAmount: amount,
        count: count,
        greeting: greeting || '恭喜发财，大吉大利！',
        claimedBy: {}, // { name: amount }
        isFullyClaimed: false,
    };
    
    chat.history.push(newPacket);
    await db.chats.put(chat);
    
    appendMessage(newPacket, chat);
    renderChatList();
    document.getElementById('red-packet-modal').classList.remove('visible');
}

/**
 * 发送专属红包
 */
async function sendDirectRedPacket() {
    const chat = state.chats[state.activeChatId];
    const amount = parseFloat(document.getElementById('rp-direct-amount').value);
    const receiverName = document.getElementById('rp-direct-receiver').value;
    const greeting = document.getElementById('rp-direct-greeting').value.trim();

    if (isNaN(amount) || amount <= 0) {
        alert("请输入有效的金额！"); return;
    }
    if (!receiverName) {
        alert("请选择一个接收人！"); return;
    }
    
    const myNickname = chat.settings.myNickname || '我';

    const newPacket = {
        role: 'user',
        senderName: myNickname,
        type: 'red_packet',
        packetType: 'direct',
        timestamp: Date.now(),
        totalAmount: amount,
        count: 1,
        greeting: greeting || '给你准备了一个红包',
        receiverName: receiverName, // 核心字段
        claimedBy: {},
        isFullyClaimed: false,
    };
    
    chat.history.push(newPacket);
    await db.chats.put(chat);

    appendMessage(newPacket, chat);
    renderChatList();
    document.getElementById('red-packet-modal').classList.remove('visible');
}

/**
 * 【总入口】当用户点击红包卡片时触发 (V4 - 流程重构版)
 * @param {number} timestamp - 被点击的红包消息的时间戳
 */
async function handlePacketClick(timestamp) {
    const currentChatId = state.activeChatId;
    const freshChat = await db.chats.get(currentChatId);
    if (!freshChat) return;

    state.chats[currentChatId] = freshChat;
    const packet = freshChat.history.find(m => m.timestamp === timestamp);
    if (!packet) return;

    const myNickname = freshChat.settings.myNickname || '我';
    const hasClaimed = packet.claimedBy && packet.claimedBy[myNickname];

    // 如果是专属红包且不是给我的，或已领完，或已领过，都只显示详情
    if ((packet.packetType === 'direct' && packet.receiverName !== myNickname) || packet.isFullyClaimed || hasClaimed) {
        showRedPacketDetails(packet);
    } else {
        // 核心流程：先尝试打开红包
        const claimedAmount = await handleOpenRedPacket(packet);
        
        // 如果成功打开（claimedAmount不为null）
        if (claimedAmount !== null) {
            // **关键：在数据更新后，再重新渲染UI**
            renderChatInterface(currentChatId);
            
            // 显示成功提示
            await showCustomAlert("恭喜！", `你领取了 ${packet.senderName} 的红包，金额为 ${claimedAmount.toFixed(2)} 元。`);
        }

        // 无论成功与否，最后都显示详情页
        // 此时需要从state中获取最新的packet对象，因为它可能在handleOpenRedPacket中被更新了
        const updatedPacket = state.chats[currentChatId].history.find(m => m.timestamp === timestamp);
        showRedPacketDetails(updatedPacket);
    }
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 【核心】处理用户打开红包的逻辑 (V5 - 专注于数据更新)
 */
async function handleOpenRedPacket(packet) {
    const chat = state.chats[state.activeChatId];
    const myNickname = chat.settings.myNickname || '我';
    
    // 1. 检查红包是否还能领
    const remainingCount = packet.count - Object.keys(packet.claimedBy || {}).length;
    if (remainingCount <= 0) {
        packet.isFullyClaimed = true;
        await db.chats.put(chat);
        await showCustomAlert("手慢了", "红包已被领完！");
        return null; // 返回null表示领取失败
    }
    
    // 2. 计算领取金额
    let claimedAmount = 0;
    const remainingAmount = packet.totalAmount - Object.values(packet.claimedBy || {}).reduce((sum, val) => sum + val, 0);
    if (packet.packetType === 'lucky') {
        if (remainingCount === 1) { claimedAmount = remainingAmount; }
        else {
            const min = 0.01;
            const max = remainingAmount - (remainingCount - 1) * min;
            claimedAmount = Math.random() * (max - min) + min;
        }
    } else { claimedAmount = packet.totalAmount; }
    claimedAmount = parseFloat(claimedAmount.toFixed(2));

    // 3. 更新红包数据
    if (!packet.claimedBy) packet.claimedBy = {};
    packet.claimedBy[myNickname] = claimedAmount;
    
    const isNowFullyClaimed = Object.keys(packet.claimedBy).length >= packet.count;
    if (isNowFullyClaimed) {
        packet.isFullyClaimed = true;
    }

    // 4. 构建系统消息和AI指令
    let hiddenMessageContent = isNowFullyClaimed
        ? `[系统提示：用户 (${myNickname}) 领取了最后一个红包，现在 ${packet.senderName} 的红包已被领完。请对此事件发表评论。]`
        : `[系统提示：用户 (${myNickname}) 刚刚领取了红包 (时间戳: ${packet.timestamp})。红包还未领完，你现在可以使用 'open_red_packet' 指令来尝试领取。]`;

    const visibleMessage = { role: 'system', type: 'pat_message', content: `你领取了 ${packet.senderName} 的红包`, timestamp: Date.now() };
    const hiddenMessage = { role: 'system', content: hiddenMessageContent, timestamp: Date.now() + 1, isHidden: true };
    chat.history.push(visibleMessage, hiddenMessage);

    // 5. 保存到数据库
    await db.chats.put(chat);
    
    // 6. 返回领取的金额，用于后续弹窗
    return claimedAmount;
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 【全新】显示红包领取详情的模态框 (V4 - 已修复参数错误)
 */
async function showRedPacketDetails(packet) {
    // 1. 直接检查传入的packet对象是否存在，无需再查找
    if (!packet) {
        console.error("showRedPacketDetails收到了无效的packet对象");
        return;
    }

    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    const modal = document.getElementById('red-packet-details-modal');
    const myNickname = chat.settings.myNickname || '我';
    
    // 2. 后续所有逻辑保持不变，直接使用传入的packet对象
    document.getElementById('rp-details-sender').textContent = packet.senderName;
    document.getElementById('rp-details-greeting').textContent = packet.greeting || '恭喜发财，大吉大利！';
    
    const myAmountEl = document.getElementById('rp-details-my-amount');
    if (packet.claimedBy && packet.claimedBy[myNickname]) {
        myAmountEl.querySelector('span:first-child').textContent = packet.claimedBy[myNickname].toFixed(2);
        myAmountEl.style.display = 'block';
    } else {
        myAmountEl.style.display = 'none';
    }

    const claimedCount = Object.keys(packet.claimedBy || {}).length;
    const claimedAmountSum = Object.values(packet.claimedBy || {}).reduce((sum, val) => sum + val, 0);
    let summaryText = `${claimedCount}/${packet.count}个红包，共${claimedAmountSum.toFixed(2)}/${packet.totalAmount.toFixed(2)}元。`;
    if (!packet.isFullyClaimed && claimedCount < packet.count) {
        const timeLeft = Math.floor((packet.timestamp + 24*60*60*1000 - Date.now()) / (1000 * 60 * 60));
        if(timeLeft > 0) summaryText += ` 剩余红包将在${timeLeft}小时内退还。`;
    }
    document.getElementById('rp-details-summary').textContent = summaryText;

    const listEl = document.getElementById('rp-details-list');
    listEl.innerHTML = '';
    const claimedEntries = Object.entries(packet.claimedBy || {});
    
    let luckyKing = { name: '', amount: -1 };
    if (packet.packetType === 'lucky' && packet.isFullyClaimed && claimedEntries.length > 1) {
        claimedEntries.forEach(([name, amount]) => {
            if (amount > luckyKing.amount) {
                luckyKing = { name, amount };
            }
        });
    }

    claimedEntries.sort((a,b) => b[1] - a[1]);

    claimedEntries.forEach(([name, amount]) => {
        const item = document.createElement('div');
        item.className = 'rp-details-item';
        let luckyTag = '';
        if (luckyKing.name && name === luckyKing.name) {
            luckyTag = '<span class="lucky-king-tag">手气王</span>';
        }
        item.innerHTML = `
            <span class="name">${name}</span>
            <span class="amount">${amount.toFixed(2)} 元</span>
            ${luckyTag}
        `;
        listEl.appendChild(item);
    });

    modal.classList.add('visible');
}
// ▲▲▲ 替换结束 ▲▲▲

// 绑定关闭详情按钮的事件
document.getElementById('close-rp-details-btn').addEventListener('click', () => {
    document.getElementById('red-packet-details-modal').classList.remove('visible');
});

// 供全局调用的函数，以便红包卡片上的 onclick 能找到它
window.handlePacketClick = handlePacketClick;

// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【全新】投票功能核心函数 ▼▼▼

/**
 * 打开创建投票的模态框并初始化
 */
function openCreatePollModal() {
    const modal = document.getElementById('create-poll-modal');
    document.getElementById('poll-question-input').value = '';
    const optionsContainer = document.getElementById('poll-options-container');
    optionsContainer.innerHTML = '';
    
    // 默认创建两个空的选项框
    addPollOptionInput();
    addPollOptionInput();
    
    modal.classList.add('visible');
}

/**
 * 在模态框中动态添加一个选项输入框
 */
function addPollOptionInput() {
    const container = document.getElementById('poll-options-container');
    const wrapper = document.createElement('div');
    wrapper.className = 'poll-option-input-wrapper';
    wrapper.innerHTML = `
        <input type="text" class="poll-option-input" placeholder="选项内容...">
        <button class="remove-option-btn">-</button>
    `;
    
    wrapper.querySelector('.remove-option-btn').addEventListener('click', () => {
        // 确保至少保留两个选项
        if (container.children.length > 2) {
            wrapper.remove();
        } else {
            alert('投票至少需要2个选项。');
        }
    });
    
    container.appendChild(wrapper);
}

/**
 * 用户确认发起投票
 */
async function sendPoll() {
    if (!state.activeChatId) return;
    
    const question = document.getElementById('poll-question-input').value.trim();
    if (!question) {
        alert('请输入投票问题！');
        return;
    }
    
    const options = Array.from(document.querySelectorAll('.poll-option-input'))
        .map(input => input.value.trim())
        .filter(text => text); // 过滤掉空的选项

    if (options.length < 2) {
        alert('请至少输入2个有效的投票选项！');
        return;
    }

    const chat = state.chats[state.activeChatId];
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    
    const newPollMessage = {
        role: 'user',
        senderName: myNickname,
        type: 'poll',
        timestamp: Date.now(),
        question: question,
        options: options,
        votes: {}, // 初始投票为空
        isClosed: false,
    };
    
    chat.history.push(newPollMessage);
    await db.chats.put(chat);
    
    appendMessage(newPollMessage, chat);
    renderChatList();
    
    document.getElementById('create-poll-modal').classList.remove('visible');
}

// ▼▼▼ 用这个【已修复重复点击问题】的版本替换 handleUserVote 函数 ▼▼▼
/**
 * 处理用户投票，并将事件作为隐藏消息存入历史记录
 * @param {number} timestamp - 投票消息的时间戳
 * @param {string} choice - 用户选择的选项文本
 */
async function handleUserVote(timestamp, choice) {
    const chat = state.chats[state.activeChatId];
    const poll = chat.history.find(m => m.timestamp === timestamp);
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';

    // 1. 【核心修正】如果投票不存在或已关闭，直接返回
    if (!poll || poll.isClosed) {
        // 如果是已关闭的投票，则直接显示结果
        if (poll && poll.isClosed) {
            showPollResults(timestamp);
        }
        return;
    }

    // 2. 检查用户是否点击了已经投过的同一个选项
    const isReclickingSameOption = poll.votes[choice] && poll.votes[choice].includes(myNickname);
    
    // 3. 【核心修正】如果不是重复点击，才执行投票逻辑
    if (!isReclickingSameOption) {
        // 移除旧投票（如果用户改选）
        for (const option in poll.votes) {
            const voterIndex = poll.votes[option].indexOf(myNickname);
            if (voterIndex > -1) {
                poll.votes[option].splice(voterIndex, 1);
            }
        }
        // 添加新投票
        if (!poll.votes[choice]) {
            poll.votes[choice] = [];
        }
        poll.votes[choice].push(myNickname);
    }
    
    // 4. 【核心逻辑】现在只处理用户投票事件，不再检查是否结束
    let hiddenMessageContent = null; 
    
    // 只有在用户真正投票或改票时，才生成提示
    if (!isReclickingSameOption) {
         hiddenMessageContent = `[系统提示：用户 (${myNickname}) 刚刚投票给了 “${choice}”。]`;
    }

    // 5. 如果有需要通知AI的事件，则创建并添加隐藏消息
    if (hiddenMessageContent) {
        const hiddenMessage = {
            role: 'system',
            content: hiddenMessageContent,
            timestamp: Date.now(),
            isHidden: true,
        };
        chat.history.push(hiddenMessage);
    }
    
    // 6. 保存数据并更新UI
    await db.chats.put(chat);
    renderChatInterface(state.activeChatId); 
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 用户结束投票，并将事件作为隐藏消息存入历史记录
 * @param {number} timestamp - 投票消息的时间戳
 */
async function endPoll(timestamp) {
    const chat = state.chats[state.activeChatId];
    const poll = chat.history.find(m => m.timestamp === timestamp);
    if (!poll || poll.isClosed) return;

    const confirmed = await showCustomConfirm("结束投票", "确定要结束这个投票吗？结束后将无法再进行投票。");
    if (confirmed) {
        poll.isClosed = true;

        const resultSummary = poll.options.map(opt => `“${opt}”(${poll.votes[opt]?.length || 0}票)`).join('，');
        const hiddenMessageContent = `[系统提示：用户手动结束了投票！最终结果为：${resultSummary}。]`;
        
        const hiddenMessage = {
            role: 'system',
            content: hiddenMessageContent,
            timestamp: Date.now(),
            isHidden: true,
        };
        chat.history.push(hiddenMessage);

        // 【核心修改】只保存数据和更新UI，不调用 triggerAiResponse()
        await db.chats.put(chat);
        renderChatInterface(state.activeChatId);
    }
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 显示投票结果详情
 * @param {number} timestamp - 投票消息的时间戳
 */
function showPollResults(timestamp) {
    const chat = state.chats[state.activeChatId];
    const poll = chat.history.find(m => m.timestamp === timestamp);
    if (!poll || !poll.isClosed) return;

    let resultsHtml = `<p><strong>${poll.question}</strong></p><hr style="opacity: 0.2; margin: 10px 0;">`;
    
    if (Object.keys(poll.votes).length === 0) {
        resultsHtml += '<p style="color: #8a8a8a;">还没有人投票。</p>';
    } else {
        poll.options.forEach(option => {
            const voters = poll.votes[option] || [];
            resultsHtml += `
                <div style="margin-bottom: 15px;">
                    <p style="font-weight: 500; margin: 0 0 5px 0;">${option} (${voters.length}票)</p>
                    <p style="font-size: 13px; color: #555; margin: 0; line-height: 1.5;">
                        ${voters.length > 0 ? voters.join('、 ') : '无人投票'}
                    </p>
                </div>
            `;
        });
    }

    showCustomAlert("投票结果", resultsHtml);
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】AI头像库管理功能函数 ▼▼▼

/**
 * 打开AI头像库管理模态框
 */
function openAiAvatarLibraryModal() {
    if (!state.activeChatId) return;
    const chat = state.chats[state.activeChatId];
    document.getElementById('ai-avatar-library-title').textContent = `“${chat.name}”的头像库`;
    renderAiAvatarLibrary();
    document.getElementById('ai-avatar-library-modal').classList.add('visible');
}

/**
 * 渲染AI头像库的内容
 */
function renderAiAvatarLibrary() {
    const grid = document.getElementById('ai-avatar-library-grid');
    grid.innerHTML = '';
    const chat = state.chats[state.activeChatId];
    const library = chat.settings.aiAvatarLibrary || [];

    if (library.length === 0) {
        grid.innerHTML = '<p style="color: var(--text-secondary); grid-column: 1 / -1; text-align: center;">这个头像库还是空的，点击右上角“添加”吧！</p>';
        return;
    }

    library.forEach((avatar, index) => {
        const item = document.createElement('div');
        item.className = 'sticker-item'; // 复用表情面板的样式
        item.style.backgroundImage = `url(${avatar.url})`;
        item.title = avatar.name;

        const deleteBtn = document.createElement('div');
        deleteBtn.className = 'delete-btn';
        deleteBtn.innerHTML = '×';
        deleteBtn.style.display = 'block'; // 总是显示删除按钮
        deleteBtn.onclick = async (e) => {
            e.stopPropagation();
            const confirmed = await showCustomConfirm('删除头像', `确定要从头像库中删除“${avatar.name}”吗？`, { confirmButtonClass: 'btn-danger' });
            if (confirmed) {
                chat.settings.aiAvatarLibrary.splice(index, 1);
                await db.chats.put(chat);
                renderAiAvatarLibrary();
            }
        };
        item.appendChild(deleteBtn);
        grid.appendChild(item);
    });
}

/**
 * 向当前AI的头像库中添加新头像
 */
async function addAvatarToLibrary() {
    const name = await showCustomPrompt("添加头像", "请为这个头像起个名字（例如：开心、哭泣）");
    if (!name || !name.trim()) return;

    const url = await showCustomPrompt("添加头像", "请输入头像的图片URL", "", "url");
    if (!url || !url.trim().startsWith('http')) {
        alert("请输入有效的图片URL！");
        return;
    }
    
    const chat = state.chats[state.activeChatId];
    if (!chat.settings.aiAvatarLibrary) {
        chat.settings.aiAvatarLibrary = [];
    }

    chat.settings.aiAvatarLibrary.push({ name: name.trim(), url: url.trim() });
    await db.chats.put(chat);
    renderAiAvatarLibrary();
}

/**
 * 关闭AI头像库管理模态框
 */
function closeAiAvatarLibraryModal() {
    document.getElementById('ai-avatar-library-modal').classList.remove('visible');
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 请将这两个【新函数】粘贴到JS功能函数定义区 ▼▼▼

/**
 * 【全新】将保存的图标URL应用到主屏幕的App图标上
 */
function applyAppIcons() {
    if (!state.globalSettings.appIcons) return;

    for (const iconId in state.globalSettings.appIcons) {
        const imgElement = document.getElementById(`icon-img-${iconId}`);
        if (imgElement) {
            imgElement.src = state.globalSettings.appIcons[iconId];
        }
    }
}

/**
 * 【全新】在外观设置页面渲染出所有App图标的设置项
 */
function renderIconSettings() {
    const grid = document.getElementById('icon-settings-grid');
    if (!grid) return;
    grid.innerHTML = '';

    const appLabels = {
        'world-book': '世界书',
        'qq': 'QQ',
        'api-settings': 'API设置',
        'wallpaper': '壁纸',
    'font': '字体',
    // ▼▼▼ 在这里添加下面这行新代码 ▼▼▼
    'check-phone': '查手机'
    };

    for (const iconId in state.globalSettings.appIcons) {
        const iconUrl = state.globalSettings.appIcons[iconId];
        const labelText = appLabels[iconId] || '未知App';

        const item = document.createElement('div');
        item.className = 'icon-setting-item';
        // 【重要】我们用 data-icon-id 来标记这个设置项对应哪个图标
        item.dataset.iconId = iconId; 

        item.innerHTML = `
            <img class="icon-preview" src="${iconUrl}" alt="${labelText}">
            <button class="change-icon-btn">更换</button>
        `;
        grid.appendChild(item);
    }
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 用这块【最终确认版】的代码，替换旧的 openBrowser 和 closeBrowser 函数 ▼▼▼

/**
 * 当用户点击链接卡片时，打开伪浏览器
 * @param {number} timestamp - 被点击消息的时间戳
 */
function openBrowser(timestamp) {
    if (!state.activeChatId) return;

    const chat = state.chats[state.activeChatId];
    // 安全检查，确保 chat 和 history 都存在
    if (!chat || !chat.history) return;

    const message = chat.history.find(m => m.timestamp === timestamp);
    if (!message || message.type !== 'share_link') {
        console.error("无法找到或消息类型不匹配的分享链接:", timestamp);
        return; // 如果找不到消息，就直接退出
    }

    // 填充浏览器内容
    document.getElementById('browser-title').textContent = message.source_name || '文章详情';
    const browserContent = document.getElementById('browser-content');
    browserContent.innerHTML = `
        <h1 class="article-title">${message.title || '无标题'}</h1>
        <div class="article-meta">
            <span>来源: ${message.source_name || '未知'}</span>
        </div>
        <div class="article-body">
            <p>${(message.content || '内容为空。').replace(/\n/g, '</p><p>')}</p>
        </div>
    `;

    // 显示浏览器屏幕
    showScreen('browser-screen');
}

/**
 * 关闭伪浏览器，返回聊天界面
 * (这个函数现在由 init() 中的事件监听器调用)
 */
function closeBrowser() {
    showScreen('chat-interface-screen'); 
}

// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【全新】用户分享链接功能的核心函数 ▼▼▼

/**
 * 打开让用户填写链接信息的模态框
 */
function openShareLinkModal() {
    if (!state.activeChatId) return;

    // 清空上次输入的内容
    document.getElementById('link-title-input').value = '';
    document.getElementById('link-description-input').value = '';
    document.getElementById('link-source-input').value = '';
    document.getElementById('link-content-input').value = '';

    // 显示模态框
    document.getElementById('share-link-modal').classList.add('visible');
}

/**
 * 用户确认分享，创建并发送链接卡片消息
 */
async function sendUserLinkShare() {
    if (!state.activeChatId) return;

    const title = document.getElementById('link-title-input').value.trim();
    if (!title) {
        alert("标题是必填项哦！");
        return;
    }

    const description = document.getElementById('link-description-input').value.trim();
    const sourceName = document.getElementById('link-source-input').value.trim();
    const content = document.getElementById('link-content-input').value.trim();

    const chat = state.chats[state.activeChatId];
    
    // 创建消息对象
    const linkMessage = {
        role: 'user', // 角色是 'user'
        type: 'share_link',
        timestamp: Date.now(),
        title: title,
        description: description,
        source_name: sourceName,
        content: content,
        // 用户分享的链接，我们不提供图片，让它总是显示占位图
        thumbnail_url: null 
    };

    // 将消息添加到历史记录
    chat.history.push(linkMessage);
    await db.chats.put(chat);

    // 渲染新消息并更新列表
    appendMessage(linkMessage, chat);
    renderChatList();

    // 关闭模态框
    document.getElementById('share-link-modal').classList.remove('visible');
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

/**
 * 根据AI的视角，过滤出它能看到的动态
 * @param {Array} allPosts - 所有待检查的动态帖子
 * @param {object} viewerChat - 正在“看”动态的那个AI的chat对象
 * @returns {Array} - 过滤后该AI可见的动态帖子
 */
function filterVisiblePostsForAI(allPosts, viewerChat) {
    if (!viewerChat || !viewerChat.id) return []; // 安全检查

    const viewerGroupId = viewerChat.groupId; // 查看者所在的分组ID

    return allPosts.filter(post => {
        // 规则1：如果是用户发的动态
        if (post.authorId === 'user') {
            // 如果用户设置了“部分可见”
            if (post.visibleGroupIds && post.visibleGroupIds.length > 0) {
                // 只有当查看者AI的分组ID在用户的可见列表里时，才可见
                return viewerGroupId && post.visibleGroupIds.includes(viewerGroupId);
            }
            // 如果用户没设置，说明是公开的，所有AI都可见
            return true;
        }

        // 规则2：如果是其他AI发的动态
        const authorGroupId = post.authorGroupId; // 发帖AI所在的分组ID
        
        // 如果发帖的AI没有分组，那它的动态就是公开的
        if (!authorGroupId) {
            return true;
        }

        // 如果发帖的AI有分组，那么只有在同一个分组的AI才能看到
        return authorGroupId === viewerGroupId;
    });
}

/**
 * 应用指定的主题（'light' 或 'dark'）
 * @param {string} theme - 要应用的主题名称
 */
function applyTheme(theme) {
    const phoneScreen = document.getElementById('phone-screen');
    const toggleSwitch = document.getElementById('theme-toggle-switch');
    
    const isDark = theme === 'dark';
    
    phoneScreen.classList.toggle('dark-mode', isDark);
    
    // 如果开关存在，就同步它的状态
    if (toggleSwitch) {
        toggleSwitch.checked = isDark;
    }
    
    localStorage.setItem('ephone-theme', theme);
}

/**
 * 切换当前的主题
 */
function toggleTheme() {
    const toggleSwitch = document.getElementById('theme-toggle-switch');
    // 直接根据开关的选中状态来决定新主题
    const newTheme = toggleSwitch.checked ? 'dark' : 'light';
    applyTheme(newTheme);
}

// ▼▼▼ 请将这【一整块新函数】粘贴到你的JS功能函数定义区 ▼▼▼

function startReplyToMessage() {
    if (!activeMessageTimestamp) return;

    const chat = state.chats[state.activeChatId];
    const message = chat.history.find(m => m.timestamp === activeMessageTimestamp);
    if (!message) return;

    // 1. 【核心修正】同时获取“完整内容”和“预览片段”
    const fullContent = String(message.content || '');
    let previewSnippet = '';

    if (typeof message.content === 'string' && STICKER_REGEX.test(message.content)) {
        previewSnippet = '[表情]';
    } else if (message.type === 'ai_image' || message.type === 'user_photo') {
        previewSnippet = '[图片]';
    } else if (message.type === 'voice_message') {
        previewSnippet = '[语音]';
    } else {
        // 预览片段依然截断，但只用于UI显示
        previewSnippet = fullContent.substring(0, 50) + (fullContent.length > 50 ? '...' : '');
    }
    
    // 2. 【核心修正】将“完整内容”存入上下文，以备发送时使用
    currentReplyContext = {
        timestamp: message.timestamp,
        senderName: message.senderName || (message.role === 'user' ? (chat.settings.myNickname || '我') : chat.name),
        content: fullContent, // <--- 这里存的是完整的原文！
    };

    // 3. 【核心修正】仅在更新“回复预览栏”时，才使用“预览片段”
    const previewBar = document.getElementById('reply-preview-bar');
    previewBar.querySelector('.sender').textContent = `回复 ${currentReplyContext.senderName}:`;
    previewBar.querySelector('.text').textContent = previewSnippet; // <--- 这里用的是缩略版！
    previewBar.style.display = 'block';

    // 4. 后续操作保持不变
    hideMessageActions();
    document.getElementById('chat-input').focus();
}

/**
 * 【全新】取消引用模式
 */
function cancelReplyMode() {
    currentReplyContext = null;
    document.getElementById('reply-preview-bar').style.display = 'none';
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】用户处理转账的核心功能函数 ▼▼▼

let activeTransferTimestamp = null; // 用于暂存被点击的转账消息的时间戳

/**
 * 显示处理转账的操作菜单
 * @param {number} timestamp - 被点击的转账消息的时间戳
 */
function showTransferActionModal(timestamp) {
    activeTransferTimestamp = timestamp;

    const chat = state.chats[state.activeChatId];
    const message = chat.history.find(m => m.timestamp === timestamp);
    if (message) {
        // 将AI的名字填入弹窗
        document.getElementById('transfer-sender-name').textContent = message.senderName;
    }
    document.getElementById('transfer-actions-modal').classList.add('visible');
}

/**
 * 隐藏处理转账的操作菜单
 */
function hideTransferActionModal() {
    document.getElementById('transfer-actions-modal').classList.remove('visible');
    activeTransferTimestamp = null;
}

/**
 * 处理用户接受或拒绝转账的逻辑
 * @param {string} choice - 用户的选择, 'accepted' 或 'declined'
 */
async function handleUserTransferResponse(choice) {
    if (!activeTransferTimestamp) return;

    const timestamp = activeTransferTimestamp;
    const chat = state.chats[state.activeChatId];
    const messageIndex = chat.history.findIndex(m => m.timestamp === timestamp);
    if (messageIndex === -1) return;

    // 1. 更新原始转账消息的状态
    const originalMessage = chat.history[messageIndex];
    originalMessage.status = choice;

    let systemContent;

    // 2. 如果用户选择“拒绝”
    if (choice === 'declined') {
        // 立刻在前端生成一个“退款”卡片，让用户看到
        const refundMessage = {
            role: 'user',
            type: 'transfer',
            isRefund: true, // 这是一个关键标记，用于UI显示这是退款
            amount: originalMessage.amount,
            note: '已拒收对方转账',
            timestamp: Date.now()
        };
        chat.history.push(refundMessage);
        
        // 准备一条对AI可见的隐藏消息，告诉它发生了什么
        systemContent = `[系统提示：你拒绝并退还了“${originalMessage.senderName}”的转账。]`;
    } else { // 如果用户选择“接受”
        // 只需准备隐藏消息通知AI即可
        systemContent = `[系统提示：你接受了“${originalMessage.senderName}”的转账。]`;
    }

    // 3. 创建这条对用户隐藏、但对AI可见的系统消息
    const hiddenMessage = {
        role: 'system',
        content: systemContent,
        timestamp: Date.now() + 1, // 保证时间戳在退款消息之后
        isHidden: true // 这个标记会让它不在聊天界面显示
    };
    chat.history.push(hiddenMessage);

    // 4. 保存所有更改到数据库，并刷新界面
    await db.chats.put(chat);
    hideTransferActionModal(); 
    renderChatInterface(state.activeChatId);
    renderChatList();
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】通话记录功能核心函数 ▼▼▼

async function renderCallHistoryScreen() {
    showScreen('call-history-screen'); // <--【核心修正】把它移动到最前面！

    const listEl = document.getElementById('call-history-list');
    const titleEl = document.getElementById('call-history-title');
    listEl.innerHTML = '';
    titleEl.textContent = '所有通话记录';
    
    const records = await db.callRecords.orderBy('timestamp').reverse().toArray();
    
    if (records.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 50px 0;">这里还没有通话记录哦~</p>';
        return; // 现在的 return 就没问题了，因为它只跳过了后续的渲染逻辑
    }
    
    records.forEach(record => {
        const card = createCallRecordCard(record);

    addLongPressListener(card, async () => {
        // 1. 弹出输入框，并将旧名称作为默认值，方便修改
        const newName = await showCustomPrompt(
            "自定义通话名称", 
            "请输入新的名称（留空则恢复默认）",
            record.customName || '' // 如果已有自定义名称，就显示它
        );

        // 2. 如果用户点击了“取消”，则什么都不做
        if (newName === null) return;
        
        // 3. 更新数据库中的这条记录
        await db.callRecords.update(record.id, { customName: newName.trim() });
        
        // 4. 刷新整个列表，让更改立刻显示出来
        await renderCallHistoryScreen();
        
        // 5. 给用户一个成功的提示
        await showCustomAlert('成功', '通话名称已更新！');
    });
        listEl.appendChild(card);
    });    
}

// ▼▼▼ 用这个【升级版】函数，完整替换你旧的 createCallRecordCard 函数 ▼▼▼
/**
 * 【升级版】根据单条记录数据，创建一张能显示聊天对象的通话卡片
 * @param {object} record - 一条通话记录对象
 * @returns {HTMLElement} - 创建好的卡片div
 */
function createCallRecordCard(record) {
    const card = document.createElement('div');
    card.className = 'call-record-card';
    card.dataset.recordId = record.id; 

    // 获取通话对象的名字
    const chatInfo = state.chats[record.chatId];
    const chatName = chatInfo ? chatInfo.name : '未知会话';

    const callDate = new Date(record.timestamp);
    const dateString = `${callDate.getFullYear()}-${String(callDate.getMonth() + 1).padStart(2, '0')}-${String(callDate.getDate()).padStart(2, '0')} ${String(callDate.getHours()).padStart(2, '0')}:${String(callDate.getMinutes()).padStart(2, '0')}`;
    const durationText = `${Math.floor(record.duration / 60)}分${record.duration % 60}秒`;

    const avatarsHtml = record.participants.map(p => 
        `<img src="${p.avatar}" alt="${p.name}" class="participant-avatar" title="${p.name}">`
    ).join('');
    
    card.innerHTML = `
        <div class="card-header">
            <span class="date">${dateString}</span>
            <span class="duration">${durationText}</span>
        </div>
        <div class="card-body">
            <!-- 【核心修改】在这里新增一个标题行 -->
            ${record.customName ? `<div class="custom-title">${record.customName}</div>` : ''}
            
            <div class="participants-info"> <!-- 新增一个容器方便布局 -->
                <div class="participants-avatars">${avatarsHtml}</div>
                <span class="participants-names">与 ${chatName}</span>
            </div>
        </div>
    `;
    return card;
}
// ▲▲▲ 替换结束 ▲▲▲

/**
 * 显示指定通话记录的完整文字稿
 * @param {number} recordId - 通话记录的ID
 */
async function showCallTranscript(recordId) {
    const record = await db.callRecords.get(recordId);
    if (!record) return;

    const modal = document.getElementById('call-transcript-modal');
    const titleEl = document.getElementById('transcript-modal-title');
    const bodyEl = document.getElementById('transcript-modal-body');

    titleEl.textContent = `通话于 ${new Date(record.timestamp).toLocaleString()} (时长: ${Math.floor(record.duration / 60)}分${record.duration % 60}秒)`;
    bodyEl.innerHTML = '';
    
    if (!record.transcript || record.transcript.length === 0) {
        bodyEl.innerHTML = '<p style="text-align:center; color: #8a8a8a;">这次通话没有留下文字记录。</p>';
    } else {
        record.transcript.forEach(entry => {
            const bubble = document.createElement('div');
            // 根据角色添加不同的class，应用不同的样式
            bubble.className = `transcript-entry ${entry.role}`; 
            bubble.textContent = entry.content;
            bodyEl.appendChild(bubble);
        });
    }

    const deleteBtn = document.getElementById('delete-transcript-btn');
    
    // 【重要】使用克隆节点技巧，防止事件重复绑定
    const newDeleteBtn = deleteBtn.cloneNode(true);
    deleteBtn.parentNode.replaceChild(newDeleteBtn, deleteBtn);
    
    // 为新的、干净的按钮绑定事件
    newDeleteBtn.addEventListener('click', async () => {
        const confirmed = await showCustomConfirm(
            "确认删除",
            "确定要永久删除这条通话记录吗？此操作不可恢复。",
            { confirmButtonClass: 'btn-danger' }
        );

        if (confirmed) {
            // 1. 关闭当前的详情弹窗
            modal.classList.remove('visible');
            
            // 2. 从数据库删除
            await db.callRecords.delete(recordId);
            
            // 3. 刷新通话记录列表
            await renderCallHistoryScreen();
            
            // 4. (可选) 给出成功提示
            alert('通话记录已删除。');
        }
    });
    modal.classList.add('visible');
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 请用这个【全新函数】替换掉你旧的 handleStatusResetClick 函数 ▼▼▼

/**
 * 【全新】处理用户点击状态栏，弹出编辑框让用户修改AI的当前状态
 */
async function handleEditStatusClick() {
    // 1. 安全检查，确保在单聊界面
    if (!state.activeChatId || state.chats[state.activeChatId].isGroup) {
        return; 
    }
    const chat = state.chats[state.activeChatId];

    // 2. 弹出输入框，让用户输入新的状态，并将当前状态作为默认值
    const newStatusText = await showCustomPrompt(
        '编辑对方状态',
        '请输入对方现在的新状态：',
        chat.status.text // 将当前状态作为输入框的默认内容
    );

    // 3. 如果用户输入了内容并点击了“确定”
    if (newStatusText !== null) {
        // 4. 更新内存和数据库中的状态数据
        chat.status.text = newStatusText.trim() || '在线'; // 如果用户清空了，就默认为“在线”
        chat.status.isBusy = false; // 每次手动编辑都默认其不处于“忙碌”状态
        chat.status.lastUpdate = Date.now();
        await db.chats.put(chat);

        // 5. 立刻刷新UI，让用户看到修改后的状态
        renderChatInterface(state.activeChatId);
        renderChatList();
        
        // 6. 给出一个无伤大雅的成功提示
        await showCustomAlert('状态已更新', `“${chat.name}”的当前状态已更新为：${chat.status.text}`);
    }
}

// 放在你的JS功能函数定义区
async function openShareTargetPicker() {
    const modal = document.getElementById('share-target-modal');
    const listEl = document.getElementById('share-target-list');
    listEl.innerHTML = '';

    // 获取所有聊天作为分享目标
    const chats = Object.values(state.chats);

    chats.forEach(chat => {
        // 复用联系人选择器的样式
        const item = document.createElement('div');
        item.className = 'contact-picker-item'; 
        item.innerHTML = `
            <input type="checkbox" class="share-target-checkbox" data-chat-id="${chat.id}" style="margin-right: 15px;">
            <img src="${chat.isGroup ? chat.settings.groupAvatar : chat.settings.aiAvatar || defaultAvatar}" class="avatar">
            <span class="name">${chat.name}</span>
        `;
        listEl.appendChild(item);
    });
    
    modal.classList.add('visible');
}

function closeMusicPlayerWithAnimation(callback) {
    const overlay = document.getElementById('music-player-overlay');
    if (!overlay.classList.contains('visible')) {
        if (callback) callback();
        return;
    }
    overlay.classList.remove('visible');
    setTimeout(() => {
        document.getElementById('music-playlist-panel').classList.remove('visible');
        if (callback) callback();
    }, 400); 
}

function parseLRC(lrcContent) {
    if (!lrcContent) return [];
    const lines = lrcContent.split('\n');
    const lyrics = [];
    const timeRegex = /\[(\d{2}):(\d{2})[.:](\d{2,3})\]/g;

    for (const line of lines) {
        const text = line.replace(timeRegex, '').trim();
        if (!text) continue;
        timeRegex.lastIndex = 0;
        let match;
        while ((match = timeRegex.exec(line)) !== null) {
            const minutes = parseInt(match[1], 10);
            const seconds = parseInt(match[2], 10);
            const milliseconds = parseInt(match[3].padEnd(3, '0'), 10);
            const time = minutes * 60 + seconds + milliseconds / 1000;
            lyrics.push({ time, text });
        }
    }
    return lyrics.sort((a, b) => a.time - b.time);
}

function renderLyrics() {
    const lyricsList = document.getElementById('music-lyrics-list');
    lyricsList.innerHTML = '';
    if (!musicState.parsedLyrics || musicState.parsedLyrics.length === 0) {
        lyricsList.innerHTML = '<div class="lyric-line">♪ 暂无歌词 ♪</div>';
        return;
    }
    musicState.parsedLyrics.forEach((line, index) => {
        const lineEl = document.createElement('div');
        lineEl.className = 'lyric-line';
        lineEl.textContent = line.text;
        lineEl.dataset.index = index;
        lyricsList.appendChild(lineEl);
    });
    lyricsList.style.transform = `translateY(0px)`;
}

function updateActiveLyric(currentTime) {
    if (musicState.parsedLyrics.length === 0) return;
    let newLyricIndex = -1;
    for (let i = 0; i < musicState.parsedLyrics.length; i++) {
        if (currentTime >= musicState.parsedLyrics[i].time) {
            newLyricIndex = i;
        } else {
            break;
        }
    }
    if (newLyricIndex === musicState.currentLyricIndex) return;
    musicState.currentLyricIndex = newLyricIndex;
    updateLyricsUI();
}

function updateLyricsUI() {
    const lyricsList = document.getElementById('music-lyrics-list');
    const container = document.getElementById('music-lyrics-container');
    const lines = lyricsList.querySelectorAll('.lyric-line');
    lines.forEach(line => line.classList.remove('active'));
    if (musicState.currentLyricIndex === -1) {
        lyricsList.style.transform = `translateY(0px)`;
        return;
    }
    const activeLine = lyricsList.querySelector(`.lyric-line[data-index="${musicState.currentLyricIndex}"]`);
    if (activeLine) {
        activeLine.classList.add('active');
        const containerHeight = container.offsetHeight;
        const offset = (containerHeight / 3) - activeLine.offsetTop - (activeLine.offsetHeight / 2);
        lyricsList.style.transform = `translateY(${offset}px)`;
    }

    // ▼▼▼ 在这里添加下面这块【新代码】 ▼▼▼
    // 【核心新增】同步歌词到悬浮栏
    const floatingLyricText = document.getElementById('floating-lyric-text');
    if (activeLine) {
        floatingLyricText.textContent = activeLine.textContent;
    } else if (musicState.parsedLyrics.length > 0) {
        floatingLyricText.textContent = '♪ ♪ ♪'; // 歌曲前奏
    } else {
        floatingLyricText.textContent = '♪ 暂无歌词 ♪';
    }
    // ▲▲▲ 新代码添加结束 ▲▲▲

}

function formatMusicTime(seconds) {
    if (isNaN(seconds) || seconds < 0) return "0:00";
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = Math.floor(seconds % 60);
    return `${minutes}:${String(remainingSeconds).padStart(2, '0')}`;
}

function updateMusicProgressBar() {
    const currentTimeEl = document.getElementById('music-current-time');
    const totalTimeEl = document.getElementById('music-total-time');
    const progressFillEl = document.getElementById('music-progress-fill');
    if (!audioPlayer.duration) {
        currentTimeEl.textContent = "0:00";
        totalTimeEl.textContent = "0:00";
        progressFillEl.style.width = '0%';
        return;
    }
    const progressPercent = (audioPlayer.currentTime / audioPlayer.duration) * 100;
    progressFillEl.style.width = `${progressPercent}%`;
    currentTimeEl.textContent = formatMusicTime(audioPlayer.currentTime);
    totalTimeEl.textContent = formatMusicTime(audioPlayer.duration);
    updateActiveLyric(audioPlayer.currentTime);
}

/**
 * 【全新】处理用户点击“撤回”按钮的入口函数
 */
async function handleRecallClick() {
    if (!activeMessageTimestamp) return;

    const RECALL_TIME_LIMIT_MS = 2 * 60 * 1000; // 设置2分钟的撤回时限
    const messageTime = activeMessageTimestamp;
    const now = Date.now();

    // 检查是否超过了撤回时限
    if (now - messageTime > RECALL_TIME_LIMIT_MS) {
        hideMessageActions();
        await showCustomAlert('操作失败', '该消息发送已超过2分钟，无法撤回。');
        return;
    }
    
    // 如果在时限内，执行真正的撤回逻辑
    await recallMessage(messageTime, true);
    hideMessageActions();
}

/**
 * 【全新】消息撤回的核心逻辑
 * @param {number} timestamp - 要撤回的消息的时间戳
 * @param {boolean} isUserRecall - 是否是用户主动撤回
 */
async function recallMessage(timestamp, isUserRecall) {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    const messageIndex = chat.history.findIndex(m => m.timestamp === timestamp);
    if (messageIndex === -1) return;

    const messageToRecall = chat.history[messageIndex];

    // 1. 修改消息对象，将其变为“已撤回”状态
    const recalledData = {
        originalType: messageToRecall.type || 'text',
        originalContent: messageToRecall.content,
        // 保存其他可能存在的原始数据
        originalMeaning: messageToRecall.meaning,
        originalQuote: messageToRecall.quote 
    };
    
    messageToRecall.type = 'recalled_message';
    messageToRecall.content = isUserRecall ? '你撤回了一条消息' : '对方撤回了一条消息';
    messageToRecall.recalledData = recalledData;
    // 清理掉不再需要的旧属性
    delete messageToRecall.meaning;
    delete messageToRecall.quote;

    // 2. 如果是用户撤回，需要给AI发送一条它看不懂内容的隐藏提示
    if (isUserRecall) {
        const hiddenMessageForAI = {
            role: 'system',
            content: `[系统提示：用户撤回了一条消息。你不知道内容是什么，只需知道这个事件即可。]`,
            timestamp: Date.now(),
            isHidden: true
        };
        chat.history.push(hiddenMessageForAI);
    }

    // 3. 保存到数据库并刷新UI
    await db.chats.put(chat);
    renderChatInterface(state.activeChatId);
    if(isUserRecall) renderChatList(); // 用户撤回时，最后一条消息变了，需要刷新列表
}

// ▼▼▼ 【全新】将这些函数粘贴到你的JS功能函数定义区 ▼▼▼

/**
 * 打开分类管理模态框
 */
async function openCategoryManager() {
    await renderCategoryListInManager();
    document.getElementById('world-book-category-manager-modal').classList.add('visible');
}

/**
 * 在模态框中渲染已存在的分类列表
 */
async function renderCategoryListInManager() {
    const listEl = document.getElementById('existing-categories-list');
    const categories = await db.worldBookCategories.toArray();
    listEl.innerHTML = '';
    if (categories.length === 0) {
        listEl.innerHTML = '<p style="text-align: center; color: var(--text-secondary);">还没有任何分类</p>';
    }
    categories.forEach(cat => {
        // 复用好友分组的样式
        const item = document.createElement('div');
        item.className = 'existing-group-item'; 
        item.innerHTML = `
            <span class="group-name">${cat.name}</span>
            <span class="delete-group-btn" data-id="${cat.id}">×</span>
        `;
        listEl.appendChild(item);
    });
}

/**
 * 添加一个新的世界书分类
 */
async function addNewCategory() {
    const input = document.getElementById('new-category-name-input');
    const name = input.value.trim();
    if (!name) {
        alert('分类名不能为空！');
        return;
    }
    const existing = await db.worldBookCategories.where('name').equals(name).first();
    if (existing) {
        alert(`分类 "${name}" 已经存在了！`);
        return;
    }
    await db.worldBookCategories.add({ name });
    input.value = '';
    await renderCategoryListInManager();
}

/**
 * 删除一个世界书分类
 * @param {number} categoryId - 要删除的分类的ID
 */
async function deleteCategory(categoryId) {
    const confirmed = await showCustomConfirm(
        '确认删除', 
        '删除分类后，该分类下的所有世界书将变为“未分类”。确定要删除吗？', 
        { confirmButtonClass: 'btn-danger' }
    );
    if (confirmed) {
        await db.worldBookCategories.delete(categoryId);
        // 将属于该分类的世界书的 categoryId 设为 null
        const booksToUpdate = await db.worldBooks.where('categoryId').equals(categoryId).toArray();
        for (const book of booksToUpdate) {
            book.categoryId = null;
            await db.worldBooks.put(book);
            const bookInState = state.worldBooks.find(wb => wb.id === book.id);
            if(bookInState) bookInState.categoryId = null;
        }
        await renderCategoryListInManager();
    }
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// --- ▼▼▼ 【全新】自定义头像框管理功能 ▼▼▼ ---

function openFrameManager() {
    renderFrameManager();
    document.getElementById('custom-frame-manager-modal').classList.add('visible');
}

async function renderFrameManager() {
    const grid = document.getElementById('custom-frame-grid');
    grid.innerHTML = '';
    const customFrames = await db.customAvatarFrames.toArray();
    if (customFrames.length === 0) {
        grid.innerHTML = '<p style="color: var(--text-secondary); grid-column: 1 / -1; text-align: center;">你还没有上传过头像框哦~</p>';
        return;
    }
    customFrames.forEach(frame => {
        const item = document.createElement('div');
        // 复用表情面板的样式，很方便
        item.className = 'sticker-item'; 
        item.style.backgroundImage = `url(${frame.url})`;
        item.title = frame.name;
        
        const deleteBtn = document.createElement('div');
        deleteBtn.className = 'delete-btn';
        deleteBtn.innerHTML = '×';
        deleteBtn.style.display = 'block';
        deleteBtn.onclick = async (e) => {
            e.stopPropagation();
            const confirmed = await showCustomConfirm('删除头像框', `确定要删除“${frame.name}”吗？`, { confirmButtonClass: 'btn-danger' });
            if (confirmed) {
                await db.customAvatarFrames.delete(frame.id);
                renderFrameManager(); // 刷新管理列表
            }
        };
        item.appendChild(deleteBtn);
        grid.appendChild(item);
    });
}

/**
 * 【V2多选版】处理用户上传自定义头像框的逻辑
 */
function handleUploadCustomFrame() {
    document.getElementById('custom-frame-upload-input').addEventListener('change', async (event) => {
        const files = event.target.files;
        if (!files.length) return;

        const newFrames = [];
        
        // 使用 for...of 循环来逐个处理选中的文件
        for (const file of files) {
            // 【核心修改】自动生成名字，而不是让用户输入
            // 我们用 "文件名 (前8位) + 时间戳" 来确保名字几乎不会重复
            const fileName = file.name.replace(/\.[^/.]+$/, "").substring(0, 8);
            const autoName = `${fileName}_${Date.now()}`;

            const base64Url = await new Promise(resolve => {
                const reader = new FileReader();
                reader.onload = e => resolve(e.target.result);
                reader.readAsDataURL(file);
            });
            
            newFrames.push({
                id: 'frame_' + (Date.now() + newFrames.length), // 确保ID唯一
                name: autoName,
                url: base64Url
            });
        }
        
        // 循环结束后，批量添加到数据库
        if (newFrames.length > 0) {
            await db.customAvatarFrames.bulkAdd(newFrames);
            renderFrameManager(); // 刷新管理列表
            await showCustomAlert("上传成功", `已成功添加 ${newFrames.length} 个新头像框！`);
        }

        // 清空文件选择器的值
        event.target.value = null;
    }, { once: true });

    document.getElementById('custom-frame-upload-input').click();
}

// ▲▲▲ 新增功能函数结束 ▲▲▲

async function openFrameSelectorModal(type, targetId = null) {
    const grid = document.getElementById('avatar-frame-grid');
    grid.innerHTML = '';
    
    currentFrameSelection.type = type;
    currentFrameSelection.target = targetId;
    
    const chat = state.chats[state.activeChatId];
    let currentFrameUrl = '';
    let previewAvatarUrl = '';

    // 确定当前使用的是哪个头像框和哪个头像
    if (type === 'ai') {
        currentFrameUrl = chat.settings.aiAvatarFrame || '';
        previewAvatarUrl = chat.settings.aiAvatar || defaultAvatar;
    } else if (type === 'my') {
        currentFrameUrl = chat.settings.myAvatarFrame || '';
        previewAvatarUrl = chat.settings.myAvatar || defaultAvatar;
    } else if (type === 'member' && targetId) {
        const member = chat.members.find(m => m.id === targetId);
        if (member) {
            currentFrameUrl = member.avatarFrame || '';
            previewAvatarUrl = member.avatar || defaultGroupMemberAvatar;
        }
    }

    // 1. 添加“无”选项
    const noFrameItem = createFrameItem({ id: 'none', url: '', name: '无' }, previewAvatarUrl);
    if (currentFrameUrl === '') noFrameItem.classList.add('selected');
    grid.appendChild(noFrameItem);

    // 2. 从数据库读取并添加所有自定义头像框
    const customFrames = await db.customAvatarFrames.toArray();
    customFrames.forEach(frame => {
        const item = createFrameItem(frame, previewAvatarUrl);
        if (currentFrameUrl === frame.url) item.classList.add('selected');
        grid.appendChild(item);
    });

    document.getElementById('avatar-frame-modal').classList.add('visible');
}

// 辅助函数：创建一个头像框选项
function createFrameItem(frame, previewAvatarSrc) {
    const item = document.createElement('div');
    item.className = 'frame-item';
    item.title = frame.name;
    item.innerHTML = `
        <img src="${previewAvatarSrc}" class="preview-avatar">
        ${frame.url ? `<img src="${frame.url}" class="preview-frame" style="pointer-events: none;">` : ''}
    `;
    item.addEventListener('click', () => {
        document.querySelectorAll('#avatar-frame-grid .frame-item').forEach(el => el.classList.remove('selected'));
        item.classList.add('selected');
        currentFrameSelection.url = frame.url;
    });
    return item;
}

// 保存选择
async function saveSelectedFrames() {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    const { type, url, target } = currentFrameSelection;

    if (type === 'ai') {
        chat.settings.aiAvatarFrame = url;
    } else if (type === 'my') {
        chat.settings.myAvatarFrame = url;
    } else if (type === 'member' && target) {
        const member = chat.members.find(m => m.id === target);
        if (member) member.avatarFrame = url;
    }
    
    await db.chats.put(chat);
    document.getElementById('avatar-frame-modal').classList.remove('visible');
    
    // 刷新UI
    renderChatInterface(state.activeChatId);
    // 如果聊天设置弹窗还开着，就刷新一下里面的头像预览
    if (document.getElementById('chat-settings-modal').classList.contains('visible')) {
        document.getElementById('chat-settings-btn').click(); 
    }
}

/**
 * 检查两个时间戳是否在不同的自然日
 * @param {number} timestamp1 - 新消息的时间戳
 * @param {number | null} timestamp2 - 上一条消息的时间戳
 * @returns {boolean} - 如果是新的一天，返回 true
 */
function isNewDay(timestamp1, timestamp2) {
    // 如果没有上一条消息的时间戳，说明这是第一条消息，肯定要显示日期
    if (!timestamp2) return true;

    const date1 = new Date(timestamp1);
    const date2 = new Date(timestamp2);

    // 比较年、月、日是否完全相同
    return date1.getFullYear() !== date2.getFullYear() ||
           date1.getMonth()    !== date2.getMonth()    ||
           date1.getDate()     !== date2.getDate();
}

/**
 * 将时间戳格式化为 "X月X日 HH:mm" 的形式
 * @param {number} timestamp - 时间戳
 * @returns {string} - 格式化后的日期字符串
 */
function formatDateStamp(timestamp) {
    const date = new Date(timestamp);
    const month = date.getMonth() + 1;
    const day = date.getDate();
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    return `${month}月${day}日 ${hours}:${minutes}`;
}

/**
 * 【全新】根据时间戳，格式化聊天列表右侧的日期/时间显示
 * @param {number} timestamp - 消息的时间戳
 * @returns {string} - 格式化后的字符串 (例如 "14:30", "昨天", "08/03")
 */
function formatChatListTimestamp(timestamp) {
    if (!timestamp) return ''; // 如果没有时间戳，返回空字符串

    const now = new Date();
    const msgDate = new Date(timestamp);

    // 判断是否为今天
    const isToday = now.getFullYear() === msgDate.getFullYear() &&
                    now.getMonth() === msgDate.getMonth() &&
                    now.getDate() === msgDate.getDate();

    if (isToday) {
        // 如果是今天，只显示时间
        return msgDate.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
    }

    // 判断是否为昨天
    const yesterday = new Date();
    yesterday.setDate(now.getDate() - 1);
    const isYesterday = yesterday.getFullYear() === msgDate.getFullYear() &&
                        yesterday.getMonth() === msgDate.getMonth() &&
                        yesterday.getDate() === msgDate.getDate();

    if (isYesterday) {
        return '昨天';
    }

    // 判断是否为今年
    if (now.getFullYear() === msgDate.getFullYear()) {
        // 如果是今年，显示 "月/日"
        const month = String(msgDate.getMonth() + 1).padStart(2, '0');
        const day = String(msgDate.getDate()).padStart(2, '0');
        return `${month}/${day}`;
    }

    // 如果是更早的年份，显示 "年/月/日"
    const year = msgDate.getFullYear();
    const month = String(msgDate.getMonth() + 1).padStart(2, '0');
    const day = String(msgDate.getDate()).padStart(2, '0');
    return `${year}/${month}/${day}`;
}

/**
 * 【全新】创建一个功能完整的日期戳“伪消息”元素
 * @param {number} timestamp - 该日期戳代表的时间
 * @returns {HTMLElement} - 创建好的 DOM 元素
 */
function createDateStampElement(timestamp) {
    // 1. 创建最外层的包裹 div，和真实消息一样
    const wrapper = document.createElement('div');
    wrapper.className = 'message-wrapper date-stamp-wrapper';
    // 【核心】把时间戳存起来，这是多选和删除的关键
    wrapper.dataset.timestamp = timestamp; 

    // 2. 创建气泡 div
    const bubble = document.createElement('div');
    // 【核心】同时加上 .message-bubble 类，让多选逻辑能找到它
    bubble.className = 'message-bubble date-stamp-bubble';
    bubble.dataset.timestamp = timestamp;
    bubble.textContent = formatDateStamp(timestamp);
    
    wrapper.appendChild(bubble);

    // 3. 【核心】为它绑定和真实消息完全一样的事件监听器
    addLongPressListener(wrapper, () => {
        // 日期戳不支持复杂操作，长按直接进入多选
        enterSelectionMode(timestamp);
    });
    wrapper.addEventListener('click', () => { 
        if (isSelectionMode) {
            toggleMessageSelection(timestamp);
        }
    });

    return wrapper;
}

// ▼▼▼ 【全新】将这三个函数粘贴到JS功能函数定义区 ▼▼▼

/**
 * 打开并准备聊天记录搜索界面
 */
function openChatSearchScreen() {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    // 清空旧的搜索条件和结果
    document.getElementById('keyword-search-input').value = '';
    document.getElementById('sender-search-select').innerHTML = '';
    document.getElementById('date-search-input').value = '';
    document.getElementById('search-results-list').innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 50px 0;">输入条件开始搜索</p>';

    // 动态填充“人物”下拉菜单
    const senderSelect = document.getElementById('sender-search-select');
    senderSelect.innerHTML = '<option value="">所有人</option>'; // 默认选项

    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    const myOption = document.createElement('option');
    myOption.value = myNickname;
    myOption.textContent = myNickname;
    senderSelect.appendChild(myOption);

    if (chat.isGroup) {
        chat.members.forEach(member => {
            const memberOption = document.createElement('option');
            memberOption.value = member.originalName; // 使用本名进行精确匹配
            memberOption.textContent = member.groupNickname; // 显示群昵称给用户看
            senderSelect.appendChild(memberOption);
        });
    } else {
        const aiOption = document.createElement('option');
        aiOption.value = chat.name;
        aiOption.textContent = chat.name;
        senderSelect.appendChild(aiOption);
    }
    
    // 关闭聊天设置弹窗，并显示搜索界面
    document.getElementById('chat-settings-modal').classList.remove('visible');
    showScreen('chat-search-screen');
}

/**
 * 执行聊天记录搜索的核心函数
 */
function performChatSearch() {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    const keyword = document.getElementById('keyword-search-input').value.trim().toLowerCase();
    const sender = document.getElementById('sender-search-select').value;
    const date = document.getElementById('date-search-input').value;

    // 如果所有条件都为空，则不显示任何结果
    if (!keyword && !sender && !date) {
        document.getElementById('search-results-list').innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 50px 0;">输入条件开始搜索</p>';
        return;
    }
    
let results = chat.history.filter(msg => !msg.isHidden);

    // 1. 按关键词过滤
    if (keyword) {
        results = results.filter(msg => {
            let contentString = '';
            if (typeof msg.content === 'string') {
                contentString = msg.content;
            } else if (msg.type === 'voice_message') {
                contentString = msg.content; // 语音消息内容
            } else if (msg.type === 'ai_image') {
                contentString = msg.content; // 图片描述
            }
            return contentString.toLowerCase().includes(keyword);
        });
    }

    // 2. 按人物过滤
    if (sender) {
        const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
        results = results.filter(msg => {
            if (msg.role === 'user') {
                return sender === myNickname;
            } else {
                return msg.senderName === sender;
            }
        });
    }

    // 3. 按日期过滤
    if (date) {
        const targetDate = new Date(date);
        const targetYear = targetDate.getFullYear();
        const targetMonth = targetDate.getMonth();
        const targetDay = targetDate.getDate();

        results = results.filter(msg => {
            const msgDate = new Date(msg.timestamp);
            return msgDate.getFullYear() === targetYear &&
                   msgDate.getMonth() === targetMonth &&
                   msgDate.getDate() === targetDay;
        });
    }

    renderSearchResults(results, keyword, chat);
}

/**
 * 渲染搜索结果列表
 * @param {Array} results - 过滤后的消息数组
 * @param {string} keyword - 用于高亮显示的关键词
 * @param {object} chat - 当前的聊天对象
 */
function renderSearchResults(results, keyword, chat) {
    const listEl = document.getElementById('search-results-list');
    listEl.innerHTML = '';

    if (results.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 50px 0;">未找到相关记录</p>';
        return;
    }

    // 结果按时间倒序显示
    results.reverse().forEach(msg => {
        let senderName, avatarSrc;
        const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
        if (msg.role === 'user') {
            senderName = myNickname;
            avatarSrc = chat.settings.myAvatar || defaultAvatar;
        } else {
            if (chat.isGroup) {
                const member = chat.members.find(m => m.originalName === msg.senderName);
                senderName = member ? member.groupNickname : msg.senderName;
                avatarSrc = member ? member.avatar : defaultGroupMemberAvatar;
            } else {
                senderName = chat.name;
                avatarSrc = chat.settings.aiAvatar || defaultAvatar;
            }
        }

        let snippet = (String(msg.content) || '[非文本消息]').substring(0, 100);
        if (keyword) {
            const regex = new RegExp(keyword, 'gi');
            snippet = snippet.replace(regex, (match) => `<span class="highlight">${match}</span>`);
        }

        const item = document.createElement('div');
        item.className = 'search-result-item';
        item.dataset.timestamp = msg.timestamp; // 关键！存储时间戳用于跳转
        item.innerHTML = `
            <img src="${avatarSrc}" class="avatar">
            <div class="result-content">
                <div class="result-header">
                    <span class="result-sender">${senderName}</span>
                    <span class="result-timestamp">${formatDateStamp(msg.timestamp)}</span>
                </div>
                <div class="result-message-snippet">${snippet}...</div>
            </div>
        `;
        listEl.appendChild(item);
    });
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

/**
 * 跳转到聊天界面中的指定消息
 * @param {number} timestamp - 目标消息的时间戳
 */
async function jumpToMessage(timestamp) {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    // 1. 切换回聊天界面
    showScreen('chat-interface-screen');

    // 2.【关键步骤】为了确保目标消息一定在DOM中，我们暂时放弃懒加载，
    //    直接渲染出当前聊天的【全部】历史记录。
    const messagesContainer = document.getElementById('chat-messages');
    messagesContainer.innerHTML = ''; // 先清空
    
    let lastMessageTimestamp = null;
    chat.history.forEach(msg => {
        if (msg.isHidden) return;
        
        // 重新渲染日期戳
        if (isNewDay(msg.timestamp, lastMessageTimestamp)) {
            const dateStampEl = createDateStampElement(msg.timestamp);
            messagesContainer.appendChild(dateStampEl);
        }
        
        // 注意：这里的 appendMessage 的第三个参数要用 true，
        // 表示是初始加载，避免不必要的进入动画。
        appendMessage(msg, chat, true); 
        lastMessageTimestamp = msg.timestamp;
    });

    // 3. 延迟一小会儿，确保DOM完全渲染完毕
    await new Promise(resolve => setTimeout(resolve, 100));

    // 4. 找到目标消息元素 (这是我们上次修改过的正确代码)
    const targetMessage = document.querySelector(`.message-bubble[data-timestamp="${timestamp}"]`)?.closest('.message-wrapper');

    if (targetMessage) {
        // 5. 平滑地滚动到目标位置
        targetMessage.scrollIntoView({ behavior: 'smooth', block: 'center' });

        // 6. 【核心新增】添加短暂的高亮效果，让用户能注意到
        const bubble = targetMessage.querySelector('.message-bubble');
        if (bubble) {
            bubble.classList.add('jump-highlight-animation');
            // 动画结束后自动移除class，方便下次再次高亮
            bubble.addEventListener('animationend', () => {
                bubble.classList.remove('jump-highlight-animation');
            }, { once: true });
        }
    } else {
        alert("无法定位到该消息，可能已被删除。");
    }
}

// ▼▼▼ 在你的JS功能函数定义区，粘贴下面这一整块新代码 ▼▼▼

// --- 美化功能的核心变量 ---
let activeThemeId = null; // 用于追踪当前正在编辑的主题ID

/**
 * 将CSS代码应用到页面上
 * @param {string} cssCode - 要应用的CSS代码字符串
 */
function applyThemeCss(cssCode) {
    const styleTag = document.getElementById('custom-theme-style');
    if (styleTag) {
        styleTag.innerHTML = cssCode || '';
    }
}

/**
 * 从数据库加载所有主题到下拉选择框
 */
async function loadThemesToDropdown() {
    const selector = document.getElementById('theme-selector');
    selector.innerHTML = '<option value="">-- 选择方案或新建 --</option>'; // 默认选项
    
    const themes = await db.themes.toArray();
    themes.forEach(theme => {
        const option = document.createElement('option');
        option.value = theme.id;
        option.textContent = theme.name;
        selector.appendChild(option);
    });
}

/**
 * 处理用户从下拉框选择一个主题的逻辑
 */
async function handleThemeSelection() {
    const selector = document.getElementById('theme-selector');
    const editor = document.getElementById('theme-css-editor');
    activeThemeId = selector.value ? parseInt(selector.value) : null;
    
    if (activeThemeId) {
        const theme = await db.themes.get(activeThemeId);
        editor.value = theme.css;
    } else {
        // 如果选择“--”，就加载模板
        editor.value = THEME_CSS_TEMPLATE;
    }
    // 立即应用选中的或模板代码，让用户看到效果
    applyThemeCss(editor.value);
}

/**
 * 保存当前编辑区的内容到当前选中的主题
 */
async function saveCurrentTheme() {
    if (!activeThemeId) {
        alert("请先选择一个方案，或使用“另存为”来创建新方案。");
        return;
    }
    const cssCode = document.getElementById('theme-css-editor').value;
    await db.themes.update(activeThemeId, { css: cssCode });
    alert("当前方案已保存！");
}

/**
 * 将当前编辑区的内容另存为一个新主题
 */
async function saveAsNewTheme() {
    const themeName = await showCustomPrompt("保存新方案", "请输入新方案的名称");
    if (!themeName || !themeName.trim()) {
        if(themeName !== null) alert("方案名称不能为空！");
        return;
    }
    const cssCode = document.getElementById('theme-css-editor').value;
    const newTheme = { name: themeName.trim(), css: cssCode };
    const newId = await db.themes.add(newTheme);
    
    // 刷新下拉框并自动选中新保存的方案
    await loadThemesToDropdown();
    document.getElementById('theme-selector').value = newId;
    activeThemeId = newId;
    
    alert(`方案 "${themeName}" 已成功保存！`);
}

/**
 * 重命名当前选中的主题
 */
async function renameSelectedTheme() {
    if (!activeThemeId) {
        alert("请先选择一个要重命名的方案。");
        return;
    }
    const currentTheme = await db.themes.get(activeThemeId);
    const newName = await showCustomPrompt("重命名方案", "请输入新的名称", currentTheme.name);
    if (newName && newName.trim()) {
        await db.themes.update(activeThemeId, { name: newName.trim() });
        await loadThemesToDropdown();
        document.getElementById('theme-selector').value = activeThemeId;
        alert("重命名成功！");
    }
}

/**
 * 删除当前选中的主题
 */
async function deleteSelectedTheme() {
    if (!activeThemeId) {
        alert("请先选择一个要删除的方案。");
        return;
    }
    const confirmed = await showCustomConfirm(
        "确认删除", 
        `确定要删除方案 "${document.getElementById('theme-selector').selectedOptions[0].textContent}" 吗？`,
        { confirmButtonClass: 'btn-danger' }
    );
    if (confirmed) {
        await db.themes.delete(activeThemeId);
        activeThemeId = null;
        await loadThemesToDropdown();
        // 恢复到模板状态
        document.getElementById('theme-css-editor').value = THEME_CSS_TEMPLATE;
        applyThemeCss(THEME_CSS_TEMPLATE);
        alert("方案已删除。");
    }
}

/**
 * 导出当前选中的主题为一个JSON文件
 */
async function exportTheme() {
    if (!activeThemeId) {
        alert("请先选择一个要导出的方案。");
        return;
    }
    const theme = await db.themes.get(activeThemeId);
    const exportData = {
        themeName: theme.name,
        themeCss: theme.css
    };
    const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${theme.name}-Theme.json`;
    a.click();
    URL.revokeObjectURL(url);
}

/**
 * 导入一个主题JSON文件
 */
function importTheme(file) {
    if (!file) return;
    const reader = new FileReader();
    reader.onload = async (e) => {
        try {
            const data = JSON.parse(e.target.result);
            if (data.themeName && typeof data.themeCss !== 'undefined') {
                // 为了避免重名，导入时可以在名字后面加个 "(导入)"
                const newTheme = {
                    name: `${data.themeName} (导入)`,
                    css: data.themeCss
                };
                const newId = await db.themes.add(newTheme);
                await loadThemesToDropdown();
                document.getElementById('theme-selector').value = newId;
                handleThemeSelection(); // 导入后自动选中并应用
                alert(`方案 "${newTheme.name}" 导入成功！`);
            } else {
                alert("导入失败：文件格式不正确。");
            }
        } catch (error) {
            alert(`导入失败：文件解析错误。 ${error.message}`);
        }
    };
    reader.readAsText(file);
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】API预设功能核心函数 ▼▼▼

/**
 * 渲染并填充API预设的下拉选择框
 */
function renderApiPresetSelector() {
    const selectEl = document.getElementById('api-preset-select');
    if (!selectEl) return;

    selectEl.innerHTML = '<option value="">-- 自定义配置 --</option>';

    if (state.apiPresets) {
        state.apiPresets.forEach(preset => {
            const option = document.createElement('option');
            option.value = preset.id;
            option.textContent = preset.name;
            selectEl.appendChild(option);
        });
    }

    // 检查当前配置是否匹配任何一个预设
    const { proxyUrl, apiKey } = state.apiConfig;
    const matchingPreset = state.apiPresets ? state.apiPresets.find(p => p.proxyUrl === proxyUrl && p.apiKey === apiKey) : null;

    if (matchingPreset) {
        selectEl.value = matchingPreset.id;
    } else {
        selectEl.value = ""; // 如果不匹配任何预设，则选中“自定义配置”
    }
}

/**
 * 当用户在下拉框中选择一个预设时触发
 */
function handleApiPresetSelectChange() {
    const selectEl = document.getElementById('api-preset-select');
    const proxyUrlInput = document.getElementById('proxy-url');
    const apiKeyInput = document.getElementById('api-key');
    const selectedId = parseInt(selectEl.value);

    if (selectedId && state.apiPresets) {
        const selectedPreset = state.apiPresets.find(p => p.id === selectedId);
        if (selectedPreset) {
            proxyUrlInput.value = selectedPreset.proxyUrl;
            apiKeyInput.value = selectedPreset.apiKey;
        }
    }
}

/**
 * 打开预设管理的操作菜单
 */
async function openApiPresetManager() {
    const selectEl = document.getElementById('api-preset-select');
    const selectedId = parseInt(selectEl.value);
    const selectedPreset = state.apiPresets ? state.apiPresets.find(p => p.id === selectedId) : null;

    const modal = document.getElementById('preset-actions-modal');
    const footer = modal.querySelector('.custom-modal-footer');

    footer.innerHTML = `
        <button id="preset-action-save-new">保存当前配置为新预设</button>
        <button id="preset-action-update-current" ${!selectedPreset ? 'disabled' : ''}>更新当前配置</button>
        <button id="preset-action-delete-current" class="btn-danger" ${!selectedPreset ? 'disabled' : ''}>删除当前配置</button>
        <button id="preset-action-cancel" style="margin-top: 8px; border-radius: 8px; background-color: #f0f0f0;">取消</button>
    `;

    document.getElementById('preset-action-save-new').addEventListener('click', saveCurrentApiConfigAsPreset);
    if (selectedPreset) {
        document.getElementById('preset-action-update-current').addEventListener('click', () => updateSelectedApiPreset(selectedId));
        document.getElementById('preset-action-delete-current').addEventListener('click', () => deleteSelectedApiPreset(selectedId));
    }
    document.getElementById('preset-action-cancel').addEventListener('click', () => modal.classList.remove('visible'));

    modal.classList.add('visible');
}

/**
 * 将当前输入框的内容保存为一个新的预设
 */
async function saveCurrentApiConfigAsPreset() {
    const proxyUrl = document.getElementById('proxy-url').value.trim();
    const apiKey = document.getElementById('api-key').value.trim();

    if (!proxyUrl || !apiKey) {
        alert('代理地址和密钥都不能为空！');
        return;
    }

    const name = await showCustomPrompt('保存API预设', '请为这个配置起个名字：');
    if (name && name.trim()) {
        const newPreset = { name: name.trim(), proxyUrl, apiKey };
        const newId = await db.apiPresets.add(newPreset);
        
        if (!state.apiPresets) state.apiPresets = [];
        state.apiPresets.push({ id: newId, ...newPreset });

        renderApiPresetSelector(); 
        document.getElementById('api-preset-select').value = newId; 
        document.getElementById('preset-actions-modal').classList.remove('visible');
        await showCustomAlert('成功', `API预设 "${name.trim()}" 已保存！`);
    }
}

/**
 * 更新当前选中的预设
 */
async function updateSelectedApiPreset(presetId) {
    const proxyUrl = document.getElementById('proxy-url').value.trim();
    const apiKey = document.getElementById('api-key').value.trim();

    if (!proxyUrl || !apiKey) {
        alert('代理地址和密钥都不能为空！');
        return;
    }

    const preset = state.apiPresets.find(p => p.id === presetId);
    if (preset) {
        preset.proxyUrl = proxyUrl;
        preset.apiKey = apiKey;
        await db.apiPresets.put(preset);
        document.getElementById('preset-actions-modal').classList.remove('visible');
        await showCustomAlert('成功', `预设 "${preset.name}" 已更新！`);
    }
}

/**
 * 删除当前选中的预设
 */
async function deleteSelectedApiPreset(presetId) {
    const preset = state.apiPresets.find(p => p.id === presetId);
    if (preset) {
        const confirmed = await showCustomConfirm('确认删除', `确定要删除API预设 "${preset.name}" 吗？`, { confirmButtonClass: 'btn-danger' });
        if (confirmed) {
            await db.apiPresets.delete(presetId);
            state.apiPresets = state.apiPresets.filter(p => p.id !== presetId);

            renderApiPresetSelector();
            document.getElementById('preset-actions-modal').classList.remove('visible');
            await showCustomAlert('成功', '预设已删除。');
        }
    }
}

// ▼▼▼ 【全新】气泡样式预设功能的核心函数 ▼▼▼

/**
 * 渲染并填充气泡样式预设的下拉选择框
 */
function renderBubblePresetSelector() {
    const selectEl = document.getElementById('bubble-style-preset-select');
    const customCssInput = document.getElementById('custom-css-input');

    selectEl.innerHTML = '<option value="">-- 无预设 --</option>';

    if (state.bubbleStylePresets) {
        state.bubbleStylePresets.forEach(preset => {
            const option = document.createElement('option');
            option.value = preset.id;
            option.textContent = preset.name;
            selectEl.appendChild(option);
        });
    }

    // 检查当前聊天的CSS是否匹配任何一个预设
    const currentCss = customCssInput.value.trim();
    const matchingPreset = state.bubbleStylePresets ? state.bubbleStylePresets.find(p => p.css.trim() === currentCss) : null;

    if (matchingPreset) {
        selectEl.value = matchingPreset.id;
    } else {
        selectEl.value = ""; // 如果不匹配任何预设，则选中“无预设”
    }
}

/**
 * 当用户在下拉框中选择一个预设时触发
 */
function handlePresetSelectChange() {
    const selectEl = document.getElementById('bubble-style-preset-select');
    const customCssInput = document.getElementById('custom-css-input');
    const selectedId = parseInt(selectEl.value);

    if (selectedId && state.bubbleStylePresets) {
        const selectedPreset = state.bubbleStylePresets.find(p => p.id === selectedId);
        if (selectedPreset) {
            customCssInput.value = selectedPreset.css;
        }
    }
    updateSettingsPreview(); // 无论如何都更新预览
}

/**
 * 打开预设管理的操作菜单
 */
async function openBubblePresetManager() {
    const selectEl = document.getElementById('bubble-style-preset-select');
    const selectedId = parseInt(selectEl.value);
    const selectedPreset = state.bubbleStylePresets ? state.bubbleStylePresets.find(p => p.id === selectedId) : null;

    const modal = document.getElementById('preset-actions-modal'); // 复用现有模态框
    const footer = modal.querySelector('.custom-modal-footer');

    footer.innerHTML = `
        <button id="preset-action-save-new">保存</button>
        <button id="preset-action-update-current" ${!selectedPreset ? 'disabled' : ''}>更新</button>
        <button id="preset-action-delete-current" class="btn-danger" ${!selectedPreset ? 'disabled' : ''}>删除</button>
        <button id="preset-action-cancel" style="margin-top: 8px; border-radius: 8px; background-color: #f0f0f0;">取消</button>
    `;

    // 重新绑定事件
    document.getElementById('preset-action-save-new').addEventListener('click', saveCurrentCssAsPreset);
    if (selectedPreset) {
        document.getElementById('preset-action-update-current').addEventListener('click', () => updateSelectedPreset(selectedId));
        document.getElementById('preset-action-delete-current').addEventListener('click', () => deleteSelectedPreset(selectedId));
    }
    document.getElementById('preset-action-cancel').addEventListener('click', () => modal.classList.remove('visible'));

    modal.classList.add('visible');
}

/**
 * 将当前CSS文本框的内容保存为一个新的预设
 */
async function saveCurrentCssAsPreset() {
    const customCssInput = document.getElementById('custom-css-input');
    const css = customCssInput.value.trim();
    if (!css) {
        alert('CSS内容不能为空！');
        return;
    }

    const name = await showCustomPrompt('保存预设', '请为这个气泡样式命名：');
    if (name && name.trim()) {
        const newPreset = { name: name.trim(), css: css };
        const newId = await db.bubbleStylePresets.add(newPreset);
        
        if (!state.bubbleStylePresets) state.bubbleStylePresets = [];
        state.bubbleStylePresets.push({ id: newId, ...newPreset });

        renderBubblePresetSelector(); 
        document.getElementById('bubble-style-preset-select').value = newId; 
        document.getElementById('preset-actions-modal').classList.remove('visible');
        await showCustomAlert('成功', `预设 "${name.trim()}" 已保存！`);
    }
}

/**
 * 更新当前选中的预设
 */
async function updateSelectedPreset(presetId) {
    const customCssInput = document.getElementById('custom-css-input');
    const css = customCssInput.value.trim();

    const preset = state.bubbleStylePresets.find(p => p.id === presetId);
    if (preset) {
        preset.css = css;
        await db.bubbleStylePresets.put(preset);
        document.getElementById('preset-actions-modal').classList.remove('visible');
        await showCustomAlert('成功', `预设 "${preset.name}" 已更新！`);
    }
}

/**
 * 删除当前选中的预设
 */
async function deleteSelectedPreset(presetId) {
    const preset = state.bubbleStylePresets.find(p => p.id === presetId);
    if (preset) {
        const confirmed = await showCustomConfirm('确认删除', `确定要删除预设 "${preset.name}" 吗？`, { confirmButtonClass: 'btn-danger' });
        if (confirmed) {
            await db.bubbleStylePresets.delete(presetId);
            state.bubbleStylePresets = state.bubbleStylePresets.filter(p => p.id !== presetId);

            renderBubblePresetSelector(); 
            document.getElementById('custom-css-input').value = '';
            updateSettingsPreview();

            document.getElementById('preset-actions-modal').classList.remove('visible');
            await showCustomAlert('成功', '预设已删除。');
        }
    }
}

// ▼▼▼ 【全新】来电铃声控制函数 ▼▼▼

/**
 * 播放来电铃声
 */
function playRingtone() {
    const ringtonePlayer = document.getElementById('ringtone-player');
    // 优先使用用户在设置中保存的URL，如果没设置，就用我们预设的URL
    const ringtoneUrl = state.globalSettings.ringtoneUrl || 'https://files.catbox.moe/3w7gla.mp3';
    
    if (ringtonePlayer && ringtoneUrl) {
        ringtonePlayer.src = ringtoneUrl;
        // play() 返回一个 Promise，我们最好用 try...catch 包裹以防止浏览器报错
        const playPromise = ringtonePlayer.play();
        if (playPromise !== undefined) {
            playPromise.catch(error => {
                console.error("铃声播放失败:", error);
                // 可以在这里给用户一个静音提示，如果需要的话
            });
        }
    }
}

/**
 * 停止并重置来电铃声
 */
function stopRingtone() {
    const ringtonePlayer = document.getElementById('ringtone-player');
    if (ringtonePlayer) {
        ringtonePlayer.pause();
        ringtonePlayer.currentTime = 0; // 将播放进度重置到开头
    }
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】音频上下文解锁函数（修复铃声无法自动播放的问题） ▼▼▼
function unlockAudioContext() {
    const ringtonePlayer = document.getElementById('ringtone-player');
    // 检查播放器是否处于暂停状态，并且我们之前没有成功播放过
    if (ringtonePlayer && ringtonePlayer.paused) {
        // 尝试播放，然后立刻暂停。
        // 这个操作对用户是无感知的，但能告诉浏览器用户已与音频交互。
        ringtonePlayer.play().catch(() => {}); // play() 会返回一个 Promise，我们忽略任何可能发生的错误
        ringtonePlayer.pause();
        console.log("Ringtone audio context unlocked.");
    }
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】锁屏功能核心函数 ▼▼▼

/**
 * 应用锁屏壁纸到 #lock-screen 元素
 */
function applyLockscreenWallpaper() {
    const lockScreen = document.getElementById('lock-screen');
    const wallpaper = state.globalSettings.lockscreenWallpaper;
    if (wallpaper && wallpaper.startsWith('data:image')) {
        lockScreen.style.backgroundImage = `url(${wallpaper})`;
    } else if (wallpaper) {
        lockScreen.style.backgroundImage = wallpaper;
    }
}

/**
 * 显示锁屏界面
 */
function lockPhone() {
    console.log("正在锁定手机...");
    isLocked = true;
    document.getElementById('lock-screen').classList.add('active');
    document.querySelectorAll('.screen:not(#lock-screen)').forEach(s => s.classList.remove('active'));
}

/**
 * 解锁手机，显示主屏幕
 */
function unlockPhone() {
    console.log("手机已解锁！");
    isLocked = false;
        // 【核心修改】在这里彻底隐藏锁屏和毛玻璃背景
    document.getElementById('lock-screen').classList.remove('active');
    const blurBg = document.getElementById('lock-screen-background-blur');
    blurBg.style.display = 'none';
    blurBg.style.opacity = '0';

    // 确保主屏幕是唯一激活的顶层屏幕
    showScreen('home-screen'); 

    // 重置锁屏的样式，为下次锁定做准备
    setTimeout(() => {
        const lockScreen = document.getElementById('lock-screen');
        const unlockHint = document.getElementById('unlock-hint');
        lockScreen.style.transition = 'none'; 
        unlockHint.style.transition = 'none';
        lockScreen.style.transform = 'translateY(0)';
        lockScreen.offsetHeight; 
        lockScreen.style.transition = 'transform 0.3s ease-out';
        unlockHint.style.transition = 'opacity 0.3s ease-out';
    }, 500); 
}

/**
 * 显示密码输入弹窗
 */
function showPasswordModal() {
    const modal = document.getElementById('password-modal-overlay');
    const input = document.getElementById('password-input-field');
    input.value = ''; // 清空上次输入
    modal.classList.add('visible');
    setTimeout(() => input.focus(), 100); // 延迟聚焦，确保动画流畅
}

/**
 * 隐藏密码输入弹窗
 */
function hidePasswordModal() {

document.getElementById('password-modal-overlay').style.backgroundImage = 'none';

    const modal = document.getElementById('password-modal-overlay');
    modal.classList.remove('visible');
    // 移除可能存在的错误动画类
    modal.querySelector('.password-modal-content').classList.remove('error');
    // 【核心修改】当取消输入密码时...
    // 1. 隐藏毛玻璃背景
    const blurBg = document.getElementById('lock-screen-background-blur');
    blurBg.style.opacity = '0';
    setTimeout(() => { blurBg.style.display = 'none'; }, 300); // 动画结束后再隐藏

    // 2. 让锁屏界面滑回来
    const lockScreen = document.getElementById('lock-screen');
    const unlockHint = document.getElementById('unlock-hint');
    lockScreen.style.transform = 'translateY(0)';
    unlockHint.style.opacity = '1';
}

/**
 * 检查用户输入的密码是否正确
 */
function checkPassword() {
    const input = document.getElementById('password-input-field');
    const enteredPassword = input.value;
    const correctPassword = state.globalSettings.password;

    if (enteredPassword === correctPassword) {
        // --- 密码正确 ---

        // 1. 【核心魔术】提前把主屏幕在最底层激活并准备好！
        //    因为它 z-index 最低，所以你暂时还看不到它。
        showScreen('home-screen');

        // 2. 隐藏密码输入框 (它会自己播放淡出动画)
        document.getElementById('password-modal-overlay').classList.remove('visible');
        
        // 3. 让毛玻璃背景也开始淡出
        document.getElementById('lock-screen-background-blur').style.opacity = '0';
        
        // 4. 等待淡出动画播放完毕 (300毫秒)，再执行最终的清理工作
        setTimeout(unlockPhone, 300);

    } else {
        // --- 密码错误 (逻辑保持不变) ---
        const content = document.querySelector('.password-modal-content');
        content.classList.add('error');
        input.value = '';
        setTimeout(() => content.classList.remove('error'), 400);
    }
}

/**
 * 更新锁屏界面的时钟
 */
function updateLockClock() {
    const now = new Date();
    const timeString = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
    const dateString = now.toLocaleDateString('zh-CN', { weekday: 'long', month: 'long', day: 'numeric' });
    document.getElementById('lock-main-time').textContent = timeString;
    document.getElementById('lock-main-date').textContent = dateString;
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 在你的JS功能函数定义区，粘贴下面这一整块新代码 ▼▼▼

/**
 * 【全新】打开批量添加表情包的模态框
 */
async function openBulkAddStickersModal() {
    const placeholder = `在这里粘贴表情包，每行一个，格式如下：\n\n猫猫喝水：https://..../cat.gif\n狗狗摇头：https://..../dog.png\n\n(支持用中文冒号“：”、英文冒号“:”或空格分隔)`;

    const textInput = await showCustomPrompt(
        "批量添加表情(URL)",
        "一行一个，名称和链接用冒号或空格隔开",
        "", // 初始值为空
        'textarea' // 使用多行文本域
    );

    if (!textInput || !textInput.trim()) {
        return; // 用户取消或没有输入
    }

    // --- 开始解析 ---
    const lines = textInput.trim().split('\n');
    const newStickers = [];
    let successCount = 0;
    let errorLines = [];

    lines.forEach((line, index) => {
        line = line.trim();
        if (!line) return; // 跳过空行

        // 容错解析：尝试用各种分隔符分割
        let parts;
        if (line.includes('：')) { // 中文冒号
            parts = line.split('：').map(p => p.trim());
        } else if (line.includes(':')) { // 英文冒号
            parts = line.split(':').map(p => p.trim());
        } else {
            // 如果没有冒号，就尝试用最后一个空格分割
            const lastSpaceIndex = line.lastIndexOf(' ');
            if (lastSpaceIndex > -1) {
                parts = [line.substring(0, lastSpaceIndex).trim(), line.substring(lastSpaceIndex + 1).trim()];
            } else {
                // 如果连空格都没有，就无法解析
                errorLines.push(index + 1);
                return;
            }
        }

        // 确保我们得到了名称和URL两部分
        if (parts.length >= 2) {
            const name = parts.slice(0, -1).join(':').trim(); // 处理名称中也包含冒号的情况
            const url = parts[parts.length - 1].trim();

            // 简单验证URL是否有效
            if (name && (url.startsWith('http') || url.startsWith('data:image'))) {
                newStickers.push({
                    id: 'sticker_' + (Date.now() + index),
                    url: url,
                    name: name
                });
                successCount++;
            } else {
                errorLines.push(index + 1);
            }
        } else {
            errorLines.push(index + 1);
        }
    });

    // --- 解析结束，准备保存 ---
    if (newStickers.length > 0) {
        await db.userStickers.bulkAdd(newStickers);
        state.userStickers.push(...newStickers);
        renderStickerPanel(); // 刷新表情面板
    }

    // --- 生成并显示报告 ---
    let reportMessage = `批量导入完成！\n\n成功导入：${successCount} 个表情。`;
    if (errorLines.length > 0) {
        reportMessage += `\n失败行号：${errorLines.join(', ')}。\n\n请检查这些行的格式是否正确。`;
    }
    await showCustomAlert("导入报告", reportMessage);
}

// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 【全新】粘贴这两个新函数到你的JS功能函数定义区 ▼▼▼

/**
 * 【全新】根据距离文本，计算CSS宽度百分比
 * @param {string} distanceText - 距离描述，例如 "500m", "10km", "很近"
 * @returns {number} - 10到90之间的百分比
 */
function calculatePinDistancePercentage(distanceText) {
    if (!distanceText) return 50; // 默认值

    const text = distanceText.toLowerCase();
    // 提取数字部分
    const matches = text.match(/(\d+(\.\d+)?)/);
    const num = matches ? parseFloat(matches[1]) : 0;

    // 根据单位或关键词判断
    if (text.includes('km') || text.includes('公里')) {
        if (num > 1000) return 90;
        if (num > 100) return 80;
        if (num > 10) return 70;
        if (num > 1) return 60;
        return 50;
    } else if (text.includes('m') || text.includes('米')) {
        if (num > 500) return 40;
        if (num > 100) return 30;
        return 20;
    } else if (text.includes('远') || text.includes('不同城市')) {
        return 90;
    } else if (text.includes('附近') || text.includes('隔壁')) {
        return 20;
    } else if (text.includes('近')) {
        return 30;
    }
    
    return 15; // 如果无法识别，给一个最小的距离
}


// ▼▼▼ 用这整块【全新代码】，替换旧的 sendUserLocation 函数 ▼▼▼

/**
 * 【全新】在定位模态框中添加一个途经点输入框
 */
function addTrajectoryPointInput(name = '') {
    const container = document.getElementById('trajectory-points-container');
    const div = document.createElement('div');
    div.style.display = 'flex';
    div.style.gap = '8px';
    div.innerHTML = `
        <input type="text" class="trajectory-point-input" placeholder="途经点${container.children.length + 1}" value="${name}" style="flex-grow: 1;">
        <button class="remove-option-btn">-</button>
    `;
    div.querySelector('.remove-option-btn').addEventListener('click', () => div.remove());
    container.appendChild(div);
}

/**
 * 【重构版】处理用户发送定位的逻辑
 */
async function sendUserLocation() {
    if (!state.activeChatId) return;

    const userLocation = document.getElementById('user-location-input').value.trim();
    const aiLocation = document.getElementById('ai-location-input').value.trim();
    const distance = document.getElementById('distance-input').value.trim();

    if (!distance || (!userLocation && !aiLocation)) {
        alert("“我的位置”和“Ta的位置”至少要填写一个，且“相距”为必填项！");
        return;
    }

    // 从所有输入框收集途经点
    const trajectoryPoints = Array.from(document.querySelectorAll('.trajectory-point-input'))
        .map(input => ({ name: input.value.trim() }))
        .filter(point => point.name); // 过滤掉空的途经点

    const chat = state.chats[state.activeChatId];
    
    const locationMessage = {
        role: 'user',
        type: 'location',
        timestamp: Date.now(),
        userLocation: userLocation,
        aiLocation: aiLocation,
        distance: distance,
        trajectoryPoints: trajectoryPoints // 使用新的数组结构
    };

    chat.history.push(locationMessage);
    await db.chats.put(chat);
    appendMessage(locationMessage, chat);
    renderChatList();

    document.getElementById('send-location-modal').classList.remove('visible');
}
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【全新】“一键重roll”功能核心代码 ▼▼▼

/**
 * 智能查找AI上一轮回复的所有消息
 * @param {Array} history - 完整的聊天历史记录
 * @returns {Array} - 一个包含了上一轮AI所有消息对象的数组
 */
function findLastAiTurnMessages(history) {
    const turnMessages = [];
    let lastMessageIndex = history.length - 1;

    // 从最后一条消息开始，向前查找
    for (let i = lastMessageIndex; i >= 0; i--) {
        const message = history[i];
        
        // 如果是AI的消息，就把它加入我们的“待删除列表”
        if (message.role === 'assistant') {
            turnMessages.unshift(message); // 使用 unshift 保持原始顺序
        } 
        // 一旦遇到非AI的消息（用户的或系统的），说明AI的这一轮回复已经结束了，立刻停止查找
        else {
            break;
        }
    }
    return turnMessages;
}

/**
 * “重roll”按钮被点击时的主处理函数
 */
async function handleRerollClick() {
    if (!state.activeChatId) return;
    const chat = state.chats[state.activeChatId];

    // 1. 调用我们的智能查找函数，找出需要删除的消息
    const messagesToReroll = findLastAiTurnMessages(chat.history);

    // 2. 如果没找到（比如最后一条是用户发的），就提示并退出
    if (messagesToReroll.length === 0) {
        alert("请在AI回复后使用此功能。");
        return;
    }

    // 3. 从聊天记录中过滤掉这些旧消息
    const timestampsToReroll = new Set(messagesToReroll.map(m => m.timestamp));
    chat.history = chat.history.filter(msg => !timestampsToReroll.has(msg.timestamp));
    
    // 4. 保存更新后的聊天记录到数据库
    await db.chats.put(chat);

    // 5. 刷新聊天界面，让旧消息瞬间消失
    renderChatInterface(state.activeChatId);

    // 6. 触发一次新的AI响应，就像用户点击了“等待回复”一样
    triggerAiResponse();
}
// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 【全新简化版】粘贴这个完整的拖动功能函数 ▼▼▼
function initDraggableLyricsBar() {
    const bar = document.getElementById('floating-lyrics-bar');
    const phoneScreen = document.getElementById('phone-screen');
    
    let isDragging = false;
    let offsetX, offsetY;

    const onDragStart = (e) => {
        // 【问题2修复】检查点击的是否是按钮，如果是，则不开始拖动
        if (e.target.closest('#lyrics-settings-btn') || e.target.closest('.close-btn')) {
            return;
        }

        isDragging = true;
        bar.classList.add('dragging');
        
        const rect = bar.getBoundingClientRect();
        const coords = getEventCoords(e);

        offsetX = coords.x - rect.left;
        offsetY = coords.y - rect.top;

        document.addEventListener('mousemove', onDragMove);
        document.addEventListener('mouseup', onDragEnd);
        document.addEventListener('touchmove', onDragMove, { passive: false });
        document.addEventListener('touchend', onDragEnd);
    };

    const onDragMove = (e) => {
        if (!isDragging) return;
        
        e.preventDefault();

        const phoneRect = phoneScreen.getBoundingClientRect();
        const coords = getEventCoords(e);

        let newLeft = coords.x - offsetX - phoneRect.left;
        let newTop = coords.y - offsetY - phoneRect.top;

        const maxLeft = phoneScreen.clientWidth - bar.offsetWidth;
        const maxTop = phoneScreen.clientHeight - bar.offsetHeight;

        newLeft = Math.max(0, Math.min(newLeft, maxLeft));
        newTop = Math.max(0, Math.min(newTop, maxTop));
        
        // 【问题1修复】在拖动时，同时设置left, top并清除transform
        bar.style.left = `${newLeft}px`;
        bar.style.top = `${newTop}px`;
        bar.style.transform = 'none';
    };

    const onDragEnd = () => {
        if (!isDragging) return;
        isDragging = false;
        bar.classList.remove('dragging');

        document.removeEventListener('mousemove', onDragMove);
        document.removeEventListener('mouseup', onDragEnd);
        document.removeEventListener('touchmove', onDragMove);
        document.removeEventListener('touchend', onDragEnd);
    };

    bar.addEventListener('mousedown', onDragStart);
    bar.addEventListener('touchstart', onDragStart, { passive: true });
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

// ▼▼▼ 【全新】粘贴这个完整的函数 ▼▼▼
function applyLyricsSettings() {
    const bar = document.getElementById('floating-lyrics-bar');
    const toggleBtn = document.getElementById('toggle-lyrics-bar-btn');
    
    // 应用样式
    bar.style.fontSize = `${lyricsBarSettings.fontSize}px`;
    bar.style.color = lyricsBarSettings.fontColor;
    bar.style.backgroundColor = `rgba(0, 0, 0, ${lyricsBarSettings.bgOpacity / 100})`;

    // 更新设置模态框里的控件值
    document.getElementById('lyrics-font-size-slider').value = lyricsBarSettings.fontSize;
    document.getElementById('lyrics-font-size-value').textContent = `${lyricsBarSettings.fontSize}px`;
    document.getElementById('lyrics-bg-opacity-slider').value = lyricsBarSettings.bgOpacity;
    document.getElementById('lyrics-bg-opacity-value').textContent = `${lyricsBarSettings.bgOpacity}%`;
    document.getElementById('lyrics-font-color-picker').value = lyricsBarSettings.fontColor;
    
    // 【问题4需要】更新播放器里的开关按钮状态
    if (toggleBtn) {
        toggleBtn.textContent = lyricsBarSettings.showOnClose ? '悬浮' : '隐藏';
        toggleBtn.style.opacity = lyricsBarSettings.showOnClose ? '1' : '0.5';
    }
}
// ▲▲▲ 新函数粘贴结束 ▲▲▲

/**
 * 【全新】获取并格式化所有未到期的约定，生成给AI看的上下文
 * @returns {Promise<string>} 格式化后的约定信息字符串
 */
async function getCountdownContext() {
    // 1. 从数据库中找出所有“约定”类型，并且目标日期还没到的记录
    const activeCountdowns = await db.memories
        .where('type').equals('countdown')
        .filter(item => item.targetDate > Date.now())
        .toArray();

    // 如果没有未到期的约定，就告诉AI“目前没有”
    if (activeCountdowns.length === 0) {
        return "\n- **近期约定**: 目前没有特别的约定。";
    }

    // 2. 如果有，就开始整理报告
    let context = "\n# 近期约定与倒计时 (重要参考信息)\n";
    const now = Date.now();

    activeCountdowns.forEach(item => {
        const diff = item.targetDate - now;
        const diffDays = Math.floor(diff / (1000 * 60 * 60 * 24));
        const diffHours = Math.floor(diff / (1000 * 60 * 60));

        let timeText;
        if (diffDays > 1) {
            timeText = `还有 ${diffDays} 天`;
        } else if (diffHours > 0) {
            timeText = `还有 ${diffHours} 小时`;
        } else {
            timeText = "就是现在！";
        }
        
        // 3. 把每一条约定都格式化成清晰的列表项
        context += `- **${item.description}**: ${timeText} (目标: ${new Date(item.targetDate).toLocaleString()})\n`;
    });

    return context;
}

// ▼▼▼ 【全新】主屏幕滑动功能JS逻辑 ▼▼▼
const homeScreen = document.getElementById('home-screen');
const homeScreenSlider = document.querySelector('.home-screen-slider');
const dots = document.querySelectorAll('.pagination-dots .dot');

let currentPage = 0;
let touchStartX = 0;
let touchEndX = 0;
let isDragging = false;

// 更新小圆点的高亮状态
function updateDots() {
    dots.forEach((dot, index) => {
        dot.classList.toggle('active', index === currentPage);
    });
}

// 滑动到指定页面
function slideToPage(pageIndex) {
    currentPage = pageIndex;
    const offset = - (currentPage * 100); // 往左移动100%
    homeScreenSlider.style.transform = `translateX(${offset}%)`; // 因为容器是200%宽，所以移动50%
    updateDots();
}

function handleTouchStart(e) {
    isDragging = true;
    touchStartX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
    homeScreenSlider.style.transition = 'none'; // 拖动时禁用动画，实现实时跟随
}

function handleTouchMove(e) {
    if (!isDragging) return;
    const currentX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
    const diffX = currentX - touchStartX;
    const baseOffset = - (currentPage * 100); // 当前页面的基础偏移量
    homeScreenSlider.style.transform = `translateX(calc(${baseOffset}% + ${diffX}px))`;
}

function handleTouchEnd(e) {
    if (!isDragging) return;
    isDragging = false;
    homeScreenSlider.style.transition = 'transform 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; // 恢复动画

    touchEndX = e.type === 'touchend' ? e.changedTouches[0].clientX : e.clientX;
    const diffX = touchEndX - touchStartX;
    const threshold = 50; // 设置一个滑动阈值，防止误触

    if (diffX < -threshold && currentPage < 1) {
        // 向左滑动，切换到下一页
        currentPage++;
    } else if (diffX > threshold && currentPage > 0) {
        // 向右滑动，切换到上一页
        currentPage--;
    }

    slideToPage(currentPage);
}

// ▲▲▲ 新JS逻辑结束 ▲▲▲

// ▼▼▼ 【全新】“查角色手机”功能的所有核心函数 ▼▼▼

/**
 * 入口：打开角色选择界面
 */
async function openCharacterSelectionScreen() {
    await renderCharacterSelectionScreen();
    showScreen('character-selection-screen');
}

/**
 * 渲染角色选择列表
 */
async function renderCharacterSelectionScreen() {
    const listEl = document.getElementById('character-selection-list');
    listEl.innerHTML = '';
    const characters = Object.values(state.chats).filter(chat => !chat.isGroup);

    if (characters.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">还没有可以查看的角色</p>';
        return;
    }

    characters.forEach(char => {
        const item = document.createElement('div');
        item.className = 'character-select-item';
        item.dataset.chatId = char.id;
        item.innerHTML = `
            <img src="${char.settings.aiAvatar || defaultAvatar}" alt="${char.name}">
            <span class="name">${char.name}</span>
        `;
        listEl.appendChild(item);
    });
}

/**
 * 【V2重构版】打开指定角色的手机界面
 */
function openCharacterPhone(chatId) {
    activeCharacterPhoneId = chatId;
    const chat = state.chats[chatId];
    if (!chat) return;

    document.getElementById('character-phone-owner-name').textContent = `${chat.name}的手机`;
    renderCharacterAppGrid(); // 渲染APP图标

    // 核心修改：我们现在显示的是“手机外壳”这个总容器
    showScreen('character-phone-container'); 
    // 同时，确保默认显示的是它的主屏幕
    showCharacterPhonePage('character-phone-screen');
}

/**
 * 【V4美化版】渲染角色手机主界面的APP图标
 */
function renderCharacterAppGrid() {
    const gridEl = document.getElementById('character-app-grid');
    gridEl.innerHTML = '';

    const apps = [
        { 
            id: 'chat', name: '微信', screen: 'character-chat-list-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#4CAF50"><path d="M12 2C6.48 2 2 6.48 2 12c0 2.94 1.28 5.58 3.34 7.42c-.22 1.4-.89 3.1-1.25 3.82c-.36.72.48 1.39 1.05.94c.82-.67 2.43-1.88 3.3-2.58C9.44 21.78 10.68 22 12 22c5.52 0 10-4.48 10-10S17.52 2 12 2zm3.5 10.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5s1.5.67 1.5 1.5s-.67 1.5-1.5 1.5zm-7 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5s1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"/></svg>`
        },
        { 
            id: 'cart', name: '购物车', screen: 'character-shopping-cart-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#F44336"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2s-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2s2-.9 2-2s-.9-2-2-2zm-15-14h3.27l.94 2H20c.69 0 1.25.56 1.25 1.25c0 .09-.02.18-.04.27l-3.58 6.49c-.25.44-.73.74-1.26.74H8.52l-.94-2H4.27V4H2V2h3.27z"/></svg>`
        },
        { 
            id: 'memos', name: '备忘录', screen: 'character-memos-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#FFC107"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>`
        },
        {
            id: 'browser', name: '浏览器', screen: 'character-browser-screen',
            // 【核心修改】替换为新的指南针SVG
            svg: `<svg viewBox="0 0 24 24" fill="#2196F3"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1.5-12.5l3 7.5 7.5-3-7.5-3z"/></svg>`
        },
        {
            id: 'album', name: '相册', screen: 'character-album-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#8BC34A"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></svg>`
        },
        {
            id: 'bank', name: '钱包', screen: 'character-bank-screen',
            // 【核心修改】替换为新的钱包SVG
            svg: `<svg viewBox="0 0 24 24" fill="#E91E63"><path d="M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></svg>`
        },
        {
            id: 'trajectory', name: '足迹', screen: 'character-trajectory-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#795548"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5s-1.12 2.5-2.5 2.5z"/></svg>`
        },
        {
            id: 'app_usage', name: '使用记录', screen: 'character-app-usage-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#607D8B"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8s-3.58 8-8 8zm.5-13H11v6l5.25 3.15l.75-1.23l-4.5-2.67z"/></svg>`
        },
        {
            id: 'diary', name: '日记', screen: 'character-diary-screen',
            svg: `<svg viewBox="0 0 24 24" fill="#009688"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83l3.75 3.75 1.83-1.83z"/></svg>`
        },
    ];

    apps.forEach(app => {
        const iconEl = document.createElement('div');
        iconEl.className = 'app-icon';
        iconEl.innerHTML = `
            <div class="icon-bg" style="display: flex; justify-content: center; align-items: center; padding: 12px;">
                ${app.svg}
            </div>
            <span class="label">${app.name}</span>
        `;
        iconEl.addEventListener('click', () => {
            switch(app.id) {
                case 'chat': renderCharacterChatList(); break;
                case 'cart': renderCharacterShoppingCart(); break;
                case 'memos': renderCharacterMemos(); break;
                case 'browser': renderCharacterBrowser(); break;
                case 'album': renderCharacterPhotoAlbum(); break;
                case 'bank': renderCharacterBank(); break;
                case 'trajectory': renderCharacterTrajectory(); break;
                case 'app_usage': renderCharacterAppUsage(); break;
            }
            showCharacterPhonePage(app.screen); 
        });
        gridEl.appendChild(iconEl);
    });
}

/**
 * 【AI核心】生成角色手机数据
 */
async function generateCharacterPhoneData() {
    if (!activeCharacterPhoneId) return;
    const chat = state.chats[activeCharacterPhoneId];
    if (!chat) return;

    // 显示加载动画
    document.getElementById('generation-overlay').classList.add('visible');

    try {
        // 1. 准备给AI的上下文
        const persona = chat.settings.aiPersona;
        const recentHistory = chat.history.slice(-10).map(msg => {
            const sender = msg.role === 'user' ? '我' : chat.name;
            return `${sender}: ${msg.content}`;
        }).join('\n');
        
        let worldBookContext = '';
        if (chat.settings.linkedWorldBookIds && chat.settings.linkedWorldBookIds.length > 0) {
            worldBookContext = '--- 世界观设定 (必须严格遵守) ---\n' +
                chat.settings.linkedWorldBookIds.map(id => {
                    const book = state.worldBooks.find(b => b.id === id);
                    return book ? `[${book.name}]: ${book.content}` : '';
                }).join('\n\n');
        }

        // 2. 构建超级Prompt (V6 精简+日记版)
        const prompt = `
# 任务
你现在是角色 "${chat.name}"。请根据你的人设、世界观以及和我的最近聊天记录，模拟生成你手机中的各项数据。你需要一次性生成所有数据，并严格按照下面的JSON格式返回。

# 你的信息
- 你的名字: ${chat.name}
- 你的人设: ${persona}
${worldBookContext}

# 和我的最近聊天记录摘要
${recentHistory}

# JSON输出格式 (必须严格遵守，不要添加任何额外说明)
{
  "chats": [
    {
      "contactName": "【这里填写你给我（用户）的备注名】"
    },
    {
      "contactName": "【虚构联系人A的名字】",
      "messages": [
        {"sender": "联系人A", "content": "消息内容1..."},
        {"sender": "${chat.name}", "content": "你的回复1..."},
        {"sender": "联系人A", "content": "消息内容2..."},
        {"sender": "联系人A", "content": "消息内容3..."},
        {"sender": "${chat.name}", "content": "你的回复2..."}
      ]
    }
  ],
  "shoppingCart": [
    {"name": "商品名", "price": 价格, "store": "店铺名"}
  ],
  "memos": [
    {"title": "备忘录标题", "content": "备忘录详细内容..."}
  ],
  "browserHistory": [
    {"query": "搜索或浏览的标题", "result": "【这里是AI生成的、关于这个搜索标题的模拟文章或网页内容】"}
  ],
  "photoAlbum": [
    {"hiddenContent": "对照片画面的详细文字描述"}
  ],
  "bank": {
    "balance": 银行卡余额(数字),
    "transactions": [
      {"type": "收入或支出", "amount": 金额, "description": "交易描述"}
    ]
  },
  "trajectory": [
    {"time": "时间段", "location": "地点", "activity": "干了什么事"}
  ],
  "appUsage": [
    {"appName": "应用名", "duration": "使用时长"}
  ]
}

# 【【【重要指令：关于聊天记录生成】】】
- 你必须为每个虚构联系人生成一段【至少包含5条消息】的对话。
- 对话内容应该自然流畅，可以包含连续发言、表情包和表情符号等，以体现真实感。
- 不要只生成一问一答的机械式对话。
`;

        // 3. 发送API请求 (这里你需要替换成你自己的API调用逻辑)
        const { proxyUrl, apiKey, model } = state.apiConfig;
        let isGemini = proxyUrl === GEMINI_API_URL;
        let geminiConfig = toGeminiRequestData(model, apiKey, prompt, [{role: 'user', content: prompt}], isGemini);

        const response = isGemini 
            ? await fetch(geminiConfig.url, geminiConfig.data) 
            : await fetch(`${proxyUrl}/v1/chat/completions`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                body: JSON.stringify({
                    model: model,
                    messages: [{role: 'user', content: prompt}],
                    temperature: 0.8,
                    response_format: { type: "json_object" } // 请求JSON输出
                })
            });

        if (!response.ok) throw new Error('API请求失败');
        
        const data = await response.json();
        const aiResponseContent = (isGemini ? data.candidates[0].content.parts[0].text : data.choices[0].message.content)
            .replace(/^```json\s*|```$/g, ''); // 清理Markdown代码块

        const newData = JSON.parse(aiResponseContent);

        // 4. 【V5最终修复版】替换并合并所有APP的数据
        let phoneData = chat.characterPhoneData;
        phoneData.lastGenerated = Date.now();
        
        // a. 【核心】清空旧的NPC聊天记录，但保留“我”的备注信息
        const userRemarkData = phoneData.chats['我'];
        phoneData.chats = { '我': userRemarkData || { avatar: '', history: [] } }; // 只保留“我”的壳

        // b. 合并AI新生成的NPC聊天数据
        if (newData.chats) {
            newData.chats.forEach(newChat => {
                // AI为“我”生成的数据，只更新备注名
                if (!newChat.messages) {
                    phoneData.chats['我'].remarkName = newChat.contactName;
                } 
                // AI为NPC生成的数据，直接存入
                else {
                    const contactName = newChat.contactName;
                    phoneData.chats[contactName] = { 
                        avatar: newChat.avatar, 
                        history: newChat.messages 
                    };
                }
            });
        }

        // c. 【核心】用“=”直接替换掉所有APP的旧数据数组
        phoneData.shoppingCart = newData.shoppingCart || [];
        phoneData.memos = newData.memos || [];
        phoneData.browserHistory = newData.browserHistory || [];
        phoneData.photoAlbum = newData.photoAlbum || [];
        phoneData.trajectory = newData.trajectory || [];
        phoneData.appUsage = newData.appUsage || [];

        // d. 【核心】同样，直接替换掉整个银行对象
        phoneData.bank = newData.bank || { balance: 0, transactions: [] };
        
        // 5. 保存到数据库
        await db.chats.put(chat);
        alert('数据已刷新！');

    } catch (error) {
        console.error("生成角色手机数据失败:", error);
        alert(`生成失败: ${error.message}`);
    } finally {
        // 隐藏加载动画
        document.getElementById('generation-overlay').classList.remove('visible');
    }
}

/**
 * 清空角色手机数据
 */
async function clearCharacterPhoneData() {
    if (!activeCharacterPhoneId) return;
    const chat = state.chats[activeCharacterPhoneId];
    if (!chat) return;

    const confirmed = await showCustomConfirm('确认清空', `确定要清空“${chat.name}”的所有手机数据吗？此操作不可恢复。`, { confirmButtonClass: 'btn-danger' });
    if (confirmed) {
        // 重置为初始状态
        chat.characterPhoneData = {
            lastGenerated: null, chats: {}, shoppingCart: [], memos: [],
            browserHistory: [], photoAlbum: [], bank: { balance: 0, transactions: [] },
            trajectory: [],         appUsage: [],
        diary: [] // <--- 在这里新增
        };
        await db.chats.put(chat);
        // 重新渲染APP网格，因为点击APP会读取新数据
        renderCharacterAppGrid();
        alert('数据已清空。');
    }
}

// --- 以下是各个APP界面的渲染函数 ---

/**
 * 【V6最终修正版】渲染角色手机的聊天列表 (修复NPC头像颜色不一致问题)
 */
function renderCharacterChatList() {
    const listEl = document.getElementById('character-chat-list');
    const characterChatData = state.chats[activeCharacterPhoneId].characterPhoneData;
    const realChatHistory = state.chats[activeCharacterPhoneId].history;
    listEl.innerHTML = '';

    // 【移除】不再需要 colorIndex，因为颜色不再依赖顺序
    // let colorIndex = 0; 
    const avatarColors = ['#FFC107', '#4CAF50', '#2196F3', '#F44336', '#9C27B0', '#00BCD4'];

    // 1. 优先渲染和“我”的聊天 (逻辑不变)
    const myChatData = characterChatData.chats['我'];
    if (myChatData) {
        const lastMsg = realChatHistory.filter(m => !m.isHidden).slice(-1)[0] || { content: '...' };
        const myChatItem = document.createElement('div');
        myChatItem.className = 'chat-list-item';
        const remarkNameForMe = myChatData.remarkName || '我';
        myChatItem.dataset.contactName = remarkNameForMe; 
        
        const myAvatar = state.chats[activeCharacterPhoneId].settings.myAvatar || defaultMyGroupAvatar;
        
        myChatItem.innerHTML = `
            <img src="${myAvatar}" class="avatar" style="border-radius: 6px;">
            <div class="info">
                <span class="name">${remarkNameForMe}</span>
                <div class="last-msg">${String(lastMsg.content).substring(0, 30)}</div>
            </div>
        `;
        listEl.appendChild(myChatItem);
    }

    // 2. 接着渲染所有和NPC的聊天
    for (const contactName in characterChatData.chats) {
        if (contactName === '我') continue;

        const contact = characterChatData.chats[contactName];
        const lastNpcMsg = contact.history.slice(-1)[0] || { content: '...' };
        const npcChatItem = document.createElement('div');
        npcChatItem.className = 'chat-list-item';
        npcChatItem.dataset.contactName = contactName;

        const npcNameInitial = contactName.slice(-1);
        
        // 【核心修正】使用和聊天详情页完全一样的颜色计算方法
        const colorIndex = contactName.length % avatarColors.length;
        const bgColor = avatarColors[colorIndex];
        
        const npcAvatarHtml = `
            <div class="avatar" style="border-radius: 6px; background-color: ${bgColor}; color: white; display: flex; justify-content: center; align-items: center; font-size: 18px; font-weight: 500;">
                ${npcNameInitial}
            </div>
        `;

        npcChatItem.innerHTML = `
            ${npcAvatarHtml}
            <div class="info">
                <span class="name">${contactName}</span>
                <div class="last-msg">${String(lastNpcMsg.content).substring(0, 30)}</div>
            </div>
        `;
        listEl.appendChild(npcChatItem);
    }

    if (listEl.children.length === 0) {
         listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">还没有聊天记录，请先刷新数据。</p>';
    }
}

/**
 * 【V6终极版】渲染角色手机的具体聊天记录 (同步列表头像样式)
 */
function renderCharacterChatHistory(contactName) {
    // 1. 获取必要的DOM元素和数据 (这部分不变)
    const messagesEl = document.getElementById('character-chat-history-messages');
    messagesEl.innerHTML = '';
    
    const characterChat = state.chats[activeCharacterPhoneId];
    if (!characterChat) return;

    // 2. 智能获取备注名 (这部分不变)
    let finalContactName = contactName;
    const myChatData = characterChat.characterPhoneData.chats['我'];
    if ((contactName === '我' || (myChatData && contactName === myChatData.remarkName)) && myChatData) {
        finalContactName = myChatData.remarkName || '我';
    }
    document.getElementById('character-chat-with-name').textContent = finalContactName;

    // 3. 【核心修改】在循环前，预先准备好两位聊天者的头像HTML
    const avatarColors = ['#FFC107', '#4CAF50', '#2196F3', '#F44336', '#9C27B0', '#00BCD4'];
    let mainCharacterAvatarHtml;
    let otherParticipantAvatarHtml;

    // a. 准备角色自己的头像 (总是真实的图片头像)
    const characterAvatarSrc = characterChat.settings.aiAvatar || defaultAvatar;
    mainCharacterAvatarHtml = `<img src="${characterAvatarSrc}" class="character-chat-avatar">`;

    // b. 根据聊天对象，准备对方的头像
    if (finalContactName === (myChatData?.remarkName || '我')) {
        // 如果是和“我”聊天，就抓取“我”的真实头像
        const myAvatarSrc = characterChat.settings.myAvatar || defaultMyGroupAvatar;
        otherParticipantAvatarHtml = `<img src="${myAvatarSrc}" class="character-chat-avatar">`;
    } else {
        // 如果是和NPC聊天，就生成文字头像
        const npcNameInitial = contactName.slice(-1);
        // 使用一个简单的哈希算法（名字长度取余）来获得一个固定的颜色，确保同一个NPC颜色不变
        const colorIndex = contactName.length % avatarColors.length;
        const bgColor = avatarColors[colorIndex];
        
        otherParticipantAvatarHtml = `
            <div class="character-chat-avatar" style="background-color: ${bgColor}; color: white; display: flex; justify-content: center; align-items: center; font-size: 18px; font-weight: 500;">
                ${npcNameInitial}
            </div>
        `;
    }

    // 4. 获取要渲染的聊天记录 (这部分不变)
    let historyToShow = [];
    if (finalContactName === (myChatData?.remarkName || '我')) {
        historyToShow = characterChat.history.slice(-20);
    } else {
        const npcChat = characterChat.characterPhoneData.chats[contactName];
        if (npcChat) {
            historyToShow = npcChat.history;
        }
    }
    
    // 5. 【核心修改】在渲染循环中，直接使用我们预先准备好的头像HTML
    const characterName = characterChat.name;
    historyToShow.forEach(msg => {
        if (msg.isHidden) return;

        const container = document.createElement('div');
        const sender = msg.role === 'user' ? '我' : (msg.sender || characterName);
        const isSentByCharacter = sender === characterName;
        
        container.className = `character-chat-bubble-container ${isSentByCharacter ? 'sent' : 'received'}`;
        
        // ★★★ 直接在这里根据发送方，选择正确的头像HTML ★★★
        const avatarHtml = isSentByCharacter ? mainCharacterAvatarHtml : otherParticipantAvatarHtml;
        
        let contentHtml = '';
        if (typeof msg.content === 'string' && STICKER_REGEX.test(msg.content)) {
            contentHtml = `<img src="${msg.content}" class="sticker-image" style="max-height: 100px;">`;
        } else {
            contentHtml = msg.content;
        }
        const bubbleHtml = `<div class="character-chat-bubble">${contentHtml}</div>`;

        container.innerHTML = `
            ${avatarHtml}
            ${bubbleHtml}
        `;
        
        messagesEl.appendChild(container);
    });

    // 滚动到底部
    messagesEl.scrollTop = messagesEl.scrollHeight;
}

/**
 * 【V2美化版】渲染角色手机 - 购物车
 */
function renderCharacterShoppingCart() {
    const listEl = document.getElementById('character-shopping-cart-list');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.shoppingCart;
    listEl.innerHTML = '';
    if (!items || items.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">购物车是空的</p>';
        return;
    }
    items.forEach(item => {
        const itemEl = document.createElement('div');
        itemEl.className = 'character-cart-item';
        itemEl.innerHTML = `
            <div class="cart-item-icon">
                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg>
            </div>
            <div class="cart-item-info">
                <div class="title">${item.name}</div>
                <div class="store">${item.store}</div>
            </div>
            <div class="cart-item-price">¥ ${item.price.toFixed(2)}</div>
        `;
        listEl.appendChild(itemEl);
    });
}

function renderCharacterMemos() {
    const listEl = document.getElementById('character-memos-list');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.memos;
    listEl.innerHTML = '';
    items.forEach(item => {
        const itemEl = document.createElement('div');
        itemEl.className = 'character-data-item';
        itemEl.innerHTML = `
            <div class="title">${item.title}</div>
            <div class="content">${item.content}</div>
        `;
        listEl.appendChild(itemEl);
    });
}

// ▲▲▲ 新功能函数结束 ▲▲▲

/**
 * 【全新】在角色手机内部切换页面
 * @param {string} pageId - 要显示的角色手机页面的ID
 */
function showCharacterPhonePage(pageId) {
    // 1. 找到角色手机内部屏幕的所有页面
    const pages = document.querySelectorAll('.character-phone-page');
    // 2. 隐藏所有页面
    pages.forEach(p => p.classList.remove('active'));
    // 3. 显示目标页面
    const pageToShow = document.getElementById(pageId);
    if (pageToShow) {
        pageToShow.classList.add('active');
    }
}

// ▼▼▼ 【全新】角色手机新增APP渲染函数 ▼▼▼

/**
 * 【V3美化版】渲染角色手机 - 浏览器历史
 */
function renderCharacterBrowser() {
    const listEl = document.getElementById('character-browser-list');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.browserHistory;
    listEl.innerHTML = '';
    if (!items || items.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">浏览器历史为空</p>';
        return;
    }
    items.forEach(item => {
        const itemEl = document.createElement('div');
        itemEl.className = 'character-browser-item';
        itemEl.innerHTML = `
            <span class="browser-item-icon">🌐</span>
            <div class="title">${item.query}</div>
        `;
        itemEl.addEventListener('click', () => {
            document.getElementById('character-browser-detail-title').textContent = item.query;
            document.getElementById('character-browser-detail-content').innerHTML = (item.result || "AI未生成详细内容。").replace(/\n/g, '<br>');
            showCharacterPhonePage('character-browser-detail-screen');
        });
        listEl.appendChild(itemEl);
    });
}

/**
 * 【V2网格版】渲染角色手机 - 相册
 */
function renderCharacterPhotoAlbum() {
    const gridEl = document.getElementById('character-album-grid');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.photoAlbum;
    gridEl.innerHTML = '';
    if (!items || items.length === 0) {
        gridEl.innerHTML = '<p style="grid-column: 1 / -1; text-align:center; color: #8a8a8a; margin-top: 50px;">相册里没有照片</p>';
        return;
    }
    items.forEach(item => {
        const itemEl = document.createElement('div');
        itemEl.className = 'character-album-item';
        // 使用固定的文字图占位符
        itemEl.innerHTML = `<img src="https://i.postimg.cc/KYr2qRCK/1.jpg" alt="文字图">`;
        // 点击时显示AI生成的详细描述
        itemEl.addEventListener('click', () => {
            // 因为publicText已经移除，标题直接设为“图片内容”
            showCustomAlert("图片内容", item.hiddenContent);
        });
        gridEl.appendChild(itemEl);
    });
}

/**
 * 【V2美化版】渲染角色手机 - 银行
 */
function renderCharacterBank() {
    const detailsEl = document.getElementById('character-bank-details');
    const bankData = state.chats[activeCharacterPhoneId].characterPhoneData.bank;
    detailsEl.innerHTML = '';
    
    const balanceCard = document.createElement('div');
    balanceCard.className = 'character-bank-balance-card';
    balanceCard.innerHTML = `
        <div class="label">账户余额</div>
        <div class="amount">¥ ${(bankData.balance || 0).toFixed(2)}</div>
    `;
    detailsEl.appendChild(balanceCard);
    
    if (!bankData.transactions || bankData.transactions.length === 0) {
        detailsEl.innerHTML += '<p style="text-align:center; color: #8a8a8a; margin-top: 30px;">暂无交易明细</p>';
        return;
    }
    // 让最新的交易在最上面
    [...bankData.transactions].reverse().forEach(item => {
        const isIncome = item.type === '收入';
        const itemEl = document.createElement('div');
        itemEl.className = 'character-bank-transaction';
        const iconBg = isIncome ? '#4CAF50' : '#E91E63';
        const iconSvg = isIncome 
            ? `<svg width="20" height="20" viewBox="0 0 24 24" fill="white"><path d="M7 10h10v4H7z" opacity=".3"/><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></svg>`
            : `<svg width="20" height="20" viewBox="0 0 24 24" fill="white"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-1 14H5c-.55 0-1-.45-1-1v-5h16v5c0 .55-.45 1-1 1zm1-10H4V7c0-.55.45-1 1-1h14c.55 0 1 .45 1 1v2z"/></svg>`;

        itemEl.innerHTML = `
            <div class="transaction-details">
                <div class="transaction-icon" style="background-color: ${iconBg};">${iconSvg}</div>
                <div>
                    <div class="title">${item.description}</div>
                    <div class="meta" style="border:none; padding:0; margin-top:4px;"><span>${item.type}</span></div>
                </div>
            </div>
            <span class="transaction-amount ${isIncome ? 'income' : 'expense'}">
                ${isIncome ? '+' : '-'} ${item.amount.toFixed(2)}
            </span>
        `;
        detailsEl.appendChild(itemEl);
    });
}

/**
 * 【V2美化版】渲染角色手机 - 行动轨迹
 */
function renderCharacterTrajectory() {
    const listEl = document.getElementById('character-trajectory-list');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.trajectory;
    listEl.innerHTML = ''; // 清空容器
    listEl.classList.add('character-trajectory-list'); // 确保容器有正确的class

    if (!items || items.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">暂无足迹</p>';
        return;
    }
    items.forEach(item => {
        const itemEl = document.createElement('div');
        itemEl.className = 'character-trajectory-item';
        itemEl.innerHTML = `
            <div class="trajectory-item-content">
                <div class="title">${item.activity}</div>
                <div class="meta">
                    <span>📍 ${item.location}</span>
                    <span style="margin-left: 10px;">🕒 ${item.time}</span>
                </div>
            </div>
        `;
        listEl.appendChild(itemEl);
    });
}

/**
 * 【V2美化版】渲染角色手机 - APP使用记录
 */
function renderCharacterAppUsage() {
    const listEl = document.getElementById('character-app-usage-list');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.appUsage;
    listEl.innerHTML = '';
     if (!items || items.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">暂无使用记录</p>';
        return;
    }

    // 1. 计算总时长，用于确定进度条比例
    const durationsInMinutes = items.map(item => parseDurationToMinutes(item.duration));
    const maxDuration = Math.max(...durationsInMinutes);

    // 2. 渲染每一项
    items.forEach((item, index) => {
        const itemEl = document.createElement('div');
        itemEl.className = 'character-app-usage-item';
        const durationInMinutes = durationsInMinutes[index];
        const barWidth = maxDuration > 0 ? (durationInMinutes / maxDuration) * 100 : 0;

        itemEl.innerHTML = `
            <div class="app-usage-header">
                <span class="name">${item.appName}</span>
                <span class="duration">${item.duration}</span>
            </div>
            <div class="app-usage-bar-container">
                <div class="app-usage-bar" style="width: ${barWidth}%;"></div>
            </div>
        `;
        listEl.appendChild(itemEl);
    });
}

// ▲▲▲ 新增渲染函数结束 ▲▲▲

/**
 * 【日记 V3-带删除和美化】渲染角色的日记列表
 */
function renderCharacterDiary() {
    const listEl = document.getElementById('character-diary-list');
    const items = state.chats[activeCharacterPhoneId].characterPhoneData.diary;
    listEl.innerHTML = '';
    if (!items || items.length === 0) {
        listEl.innerHTML = '<p style="text-align:center; color: #8a8a8a; margin-top: 50px;">日记本还是空的，点击右上角写下第一篇日记吧。</p>';
        return;
    }
    
    // 我们需要用索引来找到并删除日记，所以这里用 forEach 的第二个参数
    [...items].reverse().forEach((item, index) => {
        // 因为我们反转了数组来显示，所以需要计算出它在原始数组中的真实索引
        const originalIndex = items.length - 1 - index;

        const itemEl = document.createElement('div');
        itemEl.className = 'character-data-item';
        
        const contentHtml = renderMarkdown(item.content);
            
        itemEl.innerHTML = `
            <div class="content">${contentHtml}</div>
            <div class="meta">
                <span>${new Date(item.timestamp).toLocaleString()}</span>
            </div>
            <!-- 添加一个隐藏的删除按钮 -->
            <button class="diary-delete-btn" title="删除这篇日记">×</button>
        `;

        // 【核心】为整张日记卡片添加长按事件
        addLongPressListener(itemEl, () => {
            const deleteBtn = itemEl.querySelector('.diary-delete-btn');
            // 长按时，让删除按钮显示出来
            deleteBtn.style.opacity = '1';
        });

        // 为删除按钮本身添加点击事件
        itemEl.querySelector('.diary-delete-btn').addEventListener('click', async (e) => {
            e.stopPropagation(); // 阻止事件冒泡
            const confirmed = await showCustomConfirm(
                '删除日记',
                '确定要永久删除这篇日记吗？',
                { confirmButtonClass: 'btn-danger' }
            );
            if (confirmed) {
                // 从数据中移除
                state.chats[activeCharacterPhoneId].characterPhoneData.diary.splice(originalIndex, 1);
                // 保存到数据库
                await db.chats.put(state.chats[activeCharacterPhoneId]);
                // 重新渲染列表
                renderCharacterDiary();
            }
        });
        
        listEl.appendChild(itemEl);
    });
}

/**
 * 【日记】独立刷新，生成新的日记条目
 */
async function generateNewDiaryEntry() {
    if (!activeCharacterPhoneId) return;
    const chat = state.chats[activeCharacterPhoneId];
    if (!chat) return;

    document.getElementById('generation-overlay').classList.add('visible');

    try {
        const persona = chat.settings.aiPersona;
        const recentHistory = chat.history.slice(-20).map(msg => {
            const sender = msg.role === 'user' ? '我' : chat.name;
            return `${sender}: ${msg.content}`;
        }).join('\n');

// ▼▼▼ 在这里添加下面的代码 ▼▼▼
let worldBookContext = '';
if (chat.settings.linkedWorldBookIds && chat.settings.linkedWorldBookIds.length > 0) {
    worldBookContext = '--- 世界观设定 (这是你必须严格遵守的背景) ---\n' +
        chat.settings.linkedWorldBookIds.map(id => {
            const book = state.worldBooks.find(b => b.id === id);
            return book ? `[${book.name}]: ${book.content}` : '';
        }).join('\n\n');
}
// ▲▲▲ 添加结束 ▲▲▲
        
        // 日记的专属Prompt (V2富文本版)
        const diaryPrompt = `
# 任务
你现在是角色 "${chat.name}"。请你回顾一下最近和我的聊天，以及你的人设，然后用你的口吻写一篇日记。
这篇日记是你内心的独白，可以记录你的感受、思考、计划或者秘密。
内容要丰富、有深度，长度在100到300字之间。

# 【【【重要：格式指令】】】
你【必须】使用以下Markdown语法来丰富日记的格式，使其更具表现力：
-   **标题**: 使用 \`#\` 或 \`##\` 来创建大标题和副标题。 (例如: \`# 今天的心情\`)
-   **粗体**: 使用 \`**文字**\` 来强调重点。 (例如: \`今天真的**非常**开心。\`)
-   **斜体**: 使用 \`*文字*\` 来表达情绪或内心想法。 (例如: \`*他到底是怎么想的呢...*\`)
-   **删除线**: 使用 \`~~文字~~\` 来表示划掉或否定的想法。 (例如: \`我决定明天去<s>逛街</s>学习。\`)
-   **遮挡/剧透**: 使用 \`||文字||\` 来隐藏秘密或悄悄话。 (例如: \`我偷偷准备了一个惊喜，||是一个手织的围巾||。\`)

你的输出【必须且只能】是日记的正文内容，不要包含任何其他说明或JSON格式。

# 你的信息
- 你的名字: ${chat.name}
- 你的人设: ${persona}
${worldBookContext}

# 最近聊天记录参考
${recentHistory}
`;

        const { proxyUrl, apiKey, model } = state.apiConfig;
        let isGemini = proxyUrl === GEMINI_API_URL;
        let geminiConfig = toGeminiRequestData(model, apiKey, diaryPrompt, [{role: 'user', content: diaryPrompt}], isGemini);

        const response = isGemini 
            ? await fetch(geminiConfig.url, geminiConfig.data) 
            : await fetch(`${proxyUrl}/v1/chat/completions`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`},
                body: JSON.stringify({
                    model: model,
                    messages: [{role: 'user', content: diaryPrompt}],
                    temperature: 0.9, // 提高一点温度，让日记更有创造性
                })
            });

        if (!response.ok) throw new Error('API请求失败');
        
        const data = await response.json();
        const diaryContent = isGemini ? data.candidates[0].content.parts[0].text : data.choices[0].message.content;

        // 创建新的日记对象并保存
        const newEntry = {
            timestamp: Date.now(),
            content: diaryContent
        };
        
        chat.characterPhoneData.diary.push(newEntry);
        await db.chats.put(chat);
        
        // 刷新日记页面
        renderCharacterDiary();
        alert('新日记已生成！');

    } catch (error) {
        console.error("生成日记失败:", error);
        alert(`生成失败: ${error.message}`);
    } finally {
        document.getElementById('generation-overlay').classList.remove('visible');
    }
}

// ▲▲▲ 新增函数结束 ▲▲▲

// ▼▼▼ 【全新】万能Markdown渲染函数 (带安全过滤和遮挡效果) ▼▼▼

/**
 * 将Markdown文本安全地渲染为HTML
 * @param {string} markdownText - 原始的Markdown文本
 * @returns {string} - 处理和净化后的安全HTML字符串
 */
function renderMarkdown(markdownText) {
    if (!markdownText) return '';

    // 1. 【预处理】支持自定义的“遮挡/剧透”语法 ||spoiler||
    // 我们在 marked.js 处理之前，手动把 ||text|| 替换成带特定class的HTML标签
    let processedText = markdownText.replace(/\|\|(.*?)\|\|/g, '<span class="spoiler">$1</span>');

    // 2. 【核心】使用 marked.js 将Markdown转换为HTML
    // gfm: true 开启GitHub风格的Markdown，支持删除线等
    // breaks: true 让回车符也能变成<br>，更符合聊天习惯
    let rawHtml = marked.parse(processedText, { gfm: true, breaks: true });

    // 3. 【安全】使用 DOMPurify 清洗HTML，防止XSS攻击
    let sanitizedHtml = DOMPurify.sanitize(rawHtml);

    return sanitizedHtml;
}

// ▲▲▲ 新函数粘贴结束 ▲▲▲

/**
 * 【辅助函数】将时长字符串（如“2.5小时”）转换为分钟数
 */
function parseDurationToMinutes(durationString) {
    if (!durationString) return 0;
    const num = parseFloat(durationString) || 0;
    if (durationString.includes('小时') || durationString.includes('h')) {
        return num * 60;
    }
    // 默认单位是分钟
    return num;
}

        // ===================================================================
        // 4. 初始化函数 init()
        // ===================================================================
        async function init() {

    // ▼▼▼ 在 init() 函数的【最开头】，粘贴下面这两行代码 ▼▼▼
    const savedTheme = localStorage.getItem('ephone-theme') || 'light'; // 默认为日间模式
    applyTheme(savedTheme);
    // ▲▲▲ 粘贴结束 ▲▲▲

    // ▼▼▼ 新增代码 ▼▼▼
    const customBubbleStyleTag = document.createElement('style');
    customBubbleStyleTag.id = 'custom-bubble-style';
    document.head.appendChild(customBubbleStyleTag);
    // ▲▲▲ 新增结束 ▲▲▲

    // ▼▼▼ 新增代码 ▼▼▼
    const previewBubbleStyleTag = document.createElement('style');
    previewBubbleStyleTag.id = 'preview-bubble-style';
    document.head.appendChild(previewBubbleStyleTag);
    // ▲▲▲ 新增结束 ▲▲▲


    // ▼▼▼ 修改这两行 ▼▼▼
    applyScopedCss('', '#chat-messages', 'custom-bubble-style'); // 清除真实聊天界面的自定义样式
    applyScopedCss('', '#settings-preview-area', 'preview-bubble-style'); // 清除预览区的自定义样式
    // ▲▲▲ 修改结束 ▲▲▲

            window.showScreen = showScreen;
            window.renderChatListProxy = renderChatList;
            window.renderApiSettingsProxy = renderApiSettings;
            window.renderWallpaperScreenProxy = renderWallpaperScreen;
            window.renderWorldBookScreenProxy = renderWorldBookScreen;

            await loadAllDataFromDB();

            // 初始化未读动态计数
            const storedCount = parseInt(localStorage.getItem('unreadPostsCount')) || 0;
            updateUnreadIndicator(storedCount);
            
            // ▲▲▲ 代码添加结束 ▲▲▲

            if (state.globalSettings && state.globalSettings.fontUrl) {
                applyCustomFont(state.globalSettings.fontUrl);
            }

    // ▼▼▼ 在这里粘贴新代码 ▼▼▼
    // 【核心修复】初始化时，自动加载并应用已保存的主题
    if (state.globalSettings.activeThemeId) {
        const activeTheme = await db.themes.get(state.globalSettings.activeThemeId);
        if (activeTheme) {
            console.log(`正在应用已保存的主题: "${activeTheme.name}"`);
            applyThemeCss(activeTheme.css);
        }
    }
    // ▲▲▲ 新代码粘贴结束 ▲▲▲

            updateClock();
            setInterval(updateClock, 1000 * 30);
            applyGlobalWallpaper();
            initBatteryManager(); 

applyAppIcons();

    // ▼▼▼ 在这里添加下面这行代码 ▼▼▼
    initDraggableLyricsBar(); // 初始化悬浮歌词栏的拖动功能
    // ▲▲▲ 添加结束 ▲▲▲

            // ==========================================================
            // --- 各种事件监听器 ---
            // ==========================================================

// ▼▼▼ 在 init() 的事件监听器区域，粘贴这行新代码 ▼▼▼
document.getElementById('phone-screen').addEventListener('click', unlockAudioContext, { once: true });
// ▲▲▲ 新代码粘贴结束 ▲▲▲
            document.getElementById('custom-modal-cancel').addEventListener('click', hideCustomModal);
            document.getElementById('custom-modal-overlay').addEventListener('click', (e) => { if (e.target === modalOverlay) hideCustomModal(); });
            document.getElementById('export-data-btn').addEventListener('click', exportBackup);
            document.getElementById('import-btn').addEventListener('click', () => document.getElementById('import-data-input').click());
            document.getElementById('import-data-input').addEventListener('change', e => importBackup(e.target.files[0]));
            document.getElementById('back-to-list-btn').addEventListener('click', () => { 

    // ▼▼▼ 修改这两行 ▼▼▼
    applyScopedCss('', '#chat-messages', 'custom-bubble-style'); // 清除真实聊天界面的自定义样式
    applyScopedCss('', '#settings-preview-area', 'preview-bubble-style'); // 清除预览区的自定义样式
    // ▲▲▲ 修改结束 ▲▲▲

exitSelectionMode(); state.activeChatId = null; showScreen('chat-list-screen'); });
            
                        document.getElementById('add-chat-btn').addEventListener('click', async () => { 
                const name = await showCustomPrompt('创建新聊天', '请输入Ta的名字'); 
                if (name && name.trim()) { 
                    const newChatId = 'chat_' + Date.now(); 
                    
                    // ▼▼▼ 从这里开始替换 ▼▼▼
                    const newChat = { 
                        id: newChatId, 
                        name: name.trim(), 
                        isGroup: false,
                        isPinned: false,
                        relationship: {
                            status: 'friend',
                            blockedTimestamp: null,
                            applicationReason: ''
                        },
                        status: {
                            text: '在线',
                            lastUpdate: Date.now(),
                            isBusy: false 
                        },
                        settings: { 
                            aiPersona: '你是谁呀。', 
                            myPersona: '我是谁呀。', 
                            maxMemory: 10, 
                            aiAvatar: defaultAvatar, 
                            myAvatar: defaultAvatar, 
                            background: '', 
                            theme: 'default', 
                            fontSize: 13, 
                            customCss: '',
                            linkedWorldBookIds: [], 
                            aiAvatarLibrary: [],
                        }, 
                        history: [], 
                        musicData: { totalTime: 0 },
                        // 【核心修正】在这里为新角色初始化完整的手机数据结构
                        characterPhoneData: {
                            lastGenerated: null,
                            chats: {},
                            shoppingCart: [],
                            memos: [],
                            browserHistory: [],
                            photoAlbum: [],
                            bank: { balance: 0, transactions: [] },
                            trajectory: [],
                                                    appUsage: [],
                        diary: [] // <--- 在这里新增
                        }
                    };
                    // ▲▲▲ 替换到这里结束 ▲▲▲

                    state.chats[newChatId] = newChat; 
                    await db.chats.put(newChat); 
                    renderChatList(); 
                } 
            });

            // ▼▼▼ 【修正】创建群聊按钮现在打开联系人选择器 ▼▼▼
document.getElementById('add-group-chat-btn').addEventListener('click', openContactPickerForGroupCreate);
// ▲▲▲ 替换结束 ▲▲▲                      
            document.getElementById('transfer-cancel-btn').addEventListener('click', () => document.getElementById('transfer-modal').classList.remove('visible'));
            document.getElementById('transfer-confirm-btn').addEventListener('click', sendUserTransfer);

            document.getElementById('listen-together-btn').addEventListener('click', handleListenTogetherClick);
            document.getElementById('music-exit-btn').addEventListener('click', () => endListenTogetherSession(true));
            document.getElementById('music-return-btn').addEventListener('click', returnToChat);
            document.getElementById('music-play-pause-btn').addEventListener('click', togglePlayPause);
            document.getElementById('music-next-btn').addEventListener('click', playNext);
            document.getElementById('music-prev-btn').addEventListener('click', playPrev);
            document.getElementById('music-mode-btn').addEventListener('click', changePlayMode);
            document.getElementById('music-playlist-btn').addEventListener('click', () => { updatePlaylistUI(); document.getElementById('music-playlist-panel').classList.add('visible'); });
            document.getElementById('close-playlist-btn').addEventListener('click', () => document.getElementById('music-playlist-panel').classList.remove('visible'));
            document.getElementById('add-song-url-btn').addEventListener('click', addSongFromURL);
            document.getElementById('add-song-local-btn').addEventListener('click', () => document.getElementById('local-song-upload-input').click());
            document.getElementById('local-song-upload-input').addEventListener('change', addSongFromLocal);
            audioPlayer.addEventListener('ended', playNext);
            audioPlayer.addEventListener('pause', () => { if(musicState.isActive) { musicState.isPlaying = false; updatePlayerUI(); } });
            audioPlayer.addEventListener('play', () => { if(musicState.isActive) { musicState.isPlaying = true; updatePlayerUI(); } });

            const chatInput = document.getElementById('chat-input');
            // ▼▼▼ 找到 id="send-btn" 的 click 事件监听器 ▼▼▼
document.getElementById('send-btn').addEventListener('click', async () => { 
    const content = chatInput.value.trim(); 
    if (!content || !state.activeChatId) return; 
    
    const chat = state.chats[state.activeChatId]; 
    
    // --- 【核心修改】在这里添加 ---
    const msg = { 
        role: 'user', 
        content, 
        timestamp: Date.now() 
    };

    // 检查当前是否处于引用回复模式
    if (currentReplyContext) {
        msg.quote = currentReplyContext; // 将引用信息附加到消息对象上
    }
    // --- 【修改结束】 ---
    
    chat.history.push(msg); 
    await db.chats.put(chat); 
    appendMessage(msg, chat); 
    renderChatList(); 
    chatInput.value = ''; 
    chatInput.style.height = 'auto'; 
    chatInput.focus(); 
    
    // --- 【核心修改】发送后，取消引用模式 ---
    cancelReplyMode(); 
});
            document.getElementById('wait-reply-btn').addEventListener('click', triggerAiResponse);
            chatInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); document.getElementById('send-btn').click(); } });
            chatInput.addEventListener('input', () => { chatInput.style.height = 'auto'; chatInput.style.height = (chatInput.scrollHeight) + 'px'; });

            document.getElementById('wallpaper-upload-input').addEventListener('change', async (event) => { const file = event.target.files[0]; if(file) { const dataUrl = await new Promise((res, rej) => { const reader = new FileReader(); reader.onload = () => res(reader.result); reader.onerror = () => rej(reader.error); reader.readAsDataURL(file); }); newWallpaperBase64 = dataUrl; renderWallpaperScreen(); } });
            // ▼▼▼ 用这整块代码，替换旧的 save-wallpaper-btn 事件监听器 ▼▼▼
document.getElementById('save-wallpaper-btn').addEventListener('click', async () => {
    let changesMade = false;

    // 保存壁纸
    if (newWallpaperBase64) {
        state.globalSettings.wallpaper = newWallpaperBase64;
        changesMade = true;
    }

    // 保存锁屏壁纸 (新增)
    if (newLockscreenWallpaperBase64) {
        state.globalSettings.lockscreenWallpaper = newLockscreenWallpaperBase64;
        changesMade = true;
    }
    // 保存密码 (新增)
    const newPassword = document.getElementById('password-set-input').value;
    state.globalSettings.password = newPassword;

state.globalSettings.ringtoneUrl = document.getElementById('ringtone-url-input').value.trim();

    // ▼▼▼ 在这里添加新代码 ▼▼▼
    // 记录当前激活的主题ID
    const activeThemeSelector = document.getElementById('theme-selector');
    if (activeThemeSelector.value) {
        state.globalSettings.activeThemeId = parseInt(activeThemeSelector.value);
    } else {
        // 如果用户选择的是“-- 选择方案或新建 --”，就清空记录
        state.globalSettings.activeThemeId = null;
    }
    // ▲▲▲ 新代码添加结束 ▲▲▲
    
    await db.globalSettings.put(state.globalSettings);

    // 应用所有更改
    if (changesMade) {
        applyGlobalWallpaper();        applyLockscreenWallpaper(); // <-- 新增
        newWallpaperBase64 = null;        newLockscreenWallpaperBase64 = null; // <-- 新增
    }
    applyAppIcons(); // 重新应用所有图标

    alert('外观设置已保存并应用！');
    showScreen('home-screen');
});
// ▲▲▲ 替换结束 ▲▲▲
            document.getElementById('save-api-settings-btn').addEventListener('click', async () => { 

// ▼▼▼ 在这里粘贴新代码 ▼▼▼
const proxyUrlToCheck = document.getElementById('proxy-url').value.trim();
const isBlocked = BLOCKED_API_SITES.some(blockedDomain => proxyUrlToCheck.includes(blockedDomain));

if (isBlocked) {
    alert('错误：该 API 站点已被禁用，无法使用。');
    return; // 阻止保存
}
// ▲▲▲ 新代码粘贴结束 ▲▲▲

state.apiConfig.proxyUrl = document.getElementById('proxy-url').value.trim(); state.apiConfig.apiKey = document.getElementById('api-key').value.trim(); state.apiConfig.model = document.getElementById('model-select').value; await db.apiConfig.put(state.apiConfig); 

// 在 'save-api-settings-btn' 的 click 事件监听器内部
// await db.apiConfig.put(state.apiConfig); 这行之后

// ▼▼▼ 将之前那段保存后台活动设置的逻辑，替换为下面这个增强版 ▼▼▼

const backgroundSwitch = document.getElementById('background-activity-switch');
const intervalInput = document.getElementById('background-interval-input');
const newEnableState = backgroundSwitch.checked;
const oldEnableState = state.globalSettings.enableBackgroundActivity || false;

// 只有在用户“从关到开”时，才弹出警告
if (newEnableState && !oldEnableState) {
    const userConfirmed = confirm(
        "【高费用警告】\n\n" +
        "您正在启用“后台角色活动”功能。\n\n" +
        "这会使您的AI角色们在您不和他们聊天时，也能“独立思考”并主动给您发消息或进行社交互动，极大地增强沉浸感。\n\n" +
        "但请注意：\n" +
        "这会【在后台自动、定期地调用API】，即使您不进行任何操作。根据您的角色数量和检测间隔，这可能会导致您的API费用显著增加。\n\n" +
        "您确定要开启吗？"
    );

    if (!userConfirmed) {
        backgroundSwitch.checked = false; // 用户取消，把开关拨回去
        return; // 阻止后续逻辑
    }
}

state.globalSettings.enableBackgroundActivity = newEnableState;
state.globalSettings.backgroundActivityInterval = parseInt(intervalInput.value) || 60;
state.globalSettings.blockCooldownHours = parseFloat(document.getElementById('block-cooldown-input').value) || 1;
await db.globalSettings.put(state.globalSettings);

// 动态启动或停止模拟器
stopBackgroundSimulation();
if (state.globalSettings.enableBackgroundActivity) {
    startBackgroundSimulation();
    console.log(`后台活动模拟已启动，间隔: ${state.globalSettings.backgroundActivityInterval}秒`);
} else {
    console.log("后台活动模拟已停止。");
}
// ▲▲▲ 替换结束 ▲▲▲

alert('API设置已保存!'); });

                    // gemini 密钥聚焦的时候显示明文
        const ApiKeyInput = document.getElementById('api-key')
        ApiKeyInput.addEventListener('focus', (e) => {
            e.target.setAttribute('type', 'text')
        })
        ApiKeyInput.addEventListener('blur', (e) => {
            e.target.setAttribute('type', 'password')
        })


        document.getElementById('fetch-models-btn').addEventListener('click', async () => {
            const url = document.getElementById('proxy-url').value.trim();
            const key = document.getElementById('api-key').value.trim();
            if (!url || !key) return alert('请先填写反代地址和密钥');
            try {

                let  isGemini = url === GEMINI_API_URL;
                const response = await fetch(isGemini ? `${GEMINI_API_URL}?key=${getRandomValue(key)}` : `${url}/v1/models`,isGemini ? undefined : {headers: {'Authorization': `Bearer ${key}`}});
                if (!response.ok) throw new Error('无法获取模型列表');
                const data = await response.json();
                let models = isGemini ? data.models : data.data;
                if(isGemini){
                    models = models.map((model)=>{
                        const parts = model.name.split('/');
                        return {
                            id:parts.length > 1 ? parts[1] : model.name
                        }
                    })
                }
                const modelSelect = document.getElementById('model-select');
                modelSelect.innerHTML = '';
                models.forEach(model => {
                    const option = document.createElement('option');
                    option.value = model.id;
                    option.textContent = model.id;
                    if (model.id === state.apiConfig.model) option.selected = true;
                    modelSelect.appendChild(option);
                });
                alert('模型列表已更新');
            } catch (error) {
                alert(`拉取模型失败: ${error.message}`);
            }
        });
            document.getElementById('add-world-book-btn').addEventListener('click', async () => { const name = await showCustomPrompt('创建世界书', '请输入书名'); if (name && name.trim()) { const newBook = { id: 'wb_' + Date.now(), name: name.trim(), content: '' }; await db.worldBooks.add(newBook); state.worldBooks.push(newBook); renderWorldBookScreen(); openWorldBookEditor(newBook.id); } });
            document.getElementById('save-world-book-btn').addEventListener('click', async () => { if (!editingWorldBookId) return; const book = state.worldBooks.find(wb => wb.id === editingWorldBookId); if (book) { const newName = document.getElementById('world-book-name-input').value.trim(); if (!newName) { alert('书名不能为空！'); return; } book.name = newName; book.content = document.getElementById('world-book-content-input').value; 

        // ▼▼▼ 【核心修改】在这里保存分类ID ▼▼▼
        const categoryId = document.getElementById('world-book-category-select').value;
        // 如果选择了“未分类”，存入 null；否则存入数字ID
        book.categoryId = categoryId ? parseInt(categoryId) : null; 
        // ▲▲▲ 修改结束 ▲▲▲

await db.worldBooks.put(book); document.getElementById('world-book-editor-title').textContent = newName; editingWorldBookId = null; renderWorldBookScreen(); showScreen('world-book-screen'); } });
            document.getElementById('chat-messages').addEventListener('click', (e) => { const aiImage = e.target.closest('.ai-generated-image'); if (aiImage) { const description = aiImage.dataset.description; if (description) showCustomAlert('照片描述', description); return; }  });
            
            const chatSettingsModal = document.getElementById('chat-settings-modal');
            const worldBookSelectBox = document.querySelector('.custom-multiselect .select-box');
            const worldBookCheckboxesContainer = document.getElementById('world-book-checkboxes-container');

function updateWorldBookSelectionDisplay() { const checkedBoxes = worldBookCheckboxesContainer.querySelectorAll('input:checked'); const displayText = document.querySelector('.selected-options-text'); if (checkedBoxes.length === 0) { displayText.textContent = '-- 点击选择 --'; } else if (checkedBoxes.length > 2) { displayText.textContent = `已选择 ${checkedBoxes.length} 项`; } else { displayText.textContent = Array.from(checkedBoxes).map(cb => cb.parentElement.textContent.trim()).join(', '); } }        
            
            worldBookSelectBox.addEventListener('click', (e) => { e.stopPropagation(); worldBookCheckboxesContainer.classList.toggle('visible'); worldBookSelectBox.classList.toggle('expanded'); });
            document.getElementById('world-book-checkboxes-container').addEventListener('change', updateWorldBookSelectionDisplay);
            window.addEventListener('click', (e) => { if (!document.querySelector('.custom-multiselect').contains(e.target)) { worldBookCheckboxesContainer.classList.remove('visible'); worldBookSelectBox.classList.remove('expanded'); } });

// ▼▼▼ 请用这段【完整、全新的代码】替换旧的 chat-settings-btn 点击事件 ▼▼▼
document.getElementById('chat-settings-btn').addEventListener('click', async () => {
    if (!state.activeChatId) return;
    const chat = state.chats[state.activeChatId];
    const isGroup = chat.isGroup;

    // --- 统一显示/隐藏控件 ---
    document.getElementById('chat-name-group').style.display = 'block';
    document.getElementById('my-persona-group').style.display = 'block';
    document.getElementById('my-avatar-group').style.display = 'block';
    document.getElementById('my-group-nickname-group').style.display = isGroup ? 'block' : 'none';
    document.getElementById('group-avatar-group').style.display = isGroup ? 'block' : 'none';
    document.getElementById('group-members-group').style.display = isGroup ? 'block' : 'none';
    document.getElementById('ai-persona-group').style.display = isGroup ? 'none' : 'block';
    document.getElementById('ai-avatar-group').style.display = isGroup ? 'none' : 'block';
    
    // 【核心修改1】根据是否为群聊，显示或隐藏“好友分组”区域
    document.getElementById('assign-group-section').style.display = isGroup ? 'none' : 'block';
    
    // --- 加载表单数据 ---
    document.getElementById('chat-name-input').value = chat.name;
    document.getElementById('my-persona').value = chat.settings.myPersona;
    document.getElementById('my-avatar-preview').src = chat.settings.myAvatar || (isGroup ? defaultMyGroupAvatar : defaultAvatar);
    document.getElementById('max-memory').value = chat.settings.maxMemory;
    const bgPreview = document.getElementById('bg-preview');
    const removeBgBtn = document.getElementById('remove-bg-btn');
    if (chat.settings.background) {
        bgPreview.src = chat.settings.background;
        bgPreview.style.display = 'block';
        removeBgBtn.style.display = 'inline-block';
    } else {
        bgPreview.style.display = 'none';
        removeBgBtn.style.display = 'none';
    }

    if (isGroup) {
        document.getElementById('my-group-nickname-input').value = chat.settings.myNickname || '';
        document.getElementById('group-avatar-preview').src = chat.settings.groupAvatar || defaultGroupAvatar;
        renderGroupMemberSettings(chat.members);
    } else {
        document.getElementById('ai-persona').value = chat.settings.aiPersona;
        document.getElementById('ai-avatar-preview').src = chat.settings.aiAvatar || defaultAvatar;
        
        // 【核心修改2】如果是单聊，就加载分组列表到下拉框
        const select = document.getElementById('assign-group-select');
        select.innerHTML = '<option value="">未分组</option>'; // 清空并设置默认选项
        const groups = await db.qzoneGroups.toArray();
        groups.forEach(group => {
            const option = document.createElement('option');
            option.value = group.id;
            option.textContent = group.name;
            // 如果当前好友已经有分组，就默认选中它
            if (chat.groupId === group.id) {
                option.selected = true;
            }
            select.appendChild(option);
        }); 
    }
    
    // 加载世界书
// ▼▼▼ 用下面这段【全新逻辑】替换掉原来简单的 forEach 循环 ▼▼▼

const worldBookCheckboxesContainer = document.getElementById('world-book-checkboxes-container');
worldBookCheckboxesContainer.innerHTML = '';
const linkedIds = new Set(chat.settings.linkedWorldBookIds || []);

// 1. 获取所有分类和世界书
const categories = await db.worldBookCategories.toArray();
const books = state.worldBooks;

// 【核心改造】如果存在未分类的书籍，就创建一个“虚拟分类”
const hasUncategorized = books.some(book => !book.categoryId);
if (hasUncategorized) {
    categories.push({ id: 'uncategorized', name: '未分类' });
}

// 2. 将书籍按分类ID进行分组
const booksByCategoryId = books.reduce((acc, book) => {
    const categoryId = book.categoryId || 'uncategorized';
    if (!acc[categoryId]) {
        acc[categoryId] = [];
    }
    acc[categoryId].push(book);
    return acc;
}, {});

// 3. 遍历分类，创建带折叠功能的列表
categories.forEach(category => {
    const booksInCategory = booksByCategoryId[category.id] || [];
    if (booksInCategory.length > 0) {
        const allInCategoryChecked = booksInCategory.every(book => linkedIds.has(book.id));
        
        const header = document.createElement('div');
        header.className = 'wb-category-header';
        header.innerHTML = `
            <span class="arrow">▼</span>
            <input type="checkbox" class="wb-category-checkbox" data-category-id="${category.id}" ${allInCategoryChecked ? 'checked' : ''}>
            <span>${category.name}</span>
        `;
        
        const bookContainer = document.createElement('div');
        bookContainer.className = 'wb-book-container';
        bookContainer.dataset.containerFor = category.id;

        booksInCategory.forEach(book => {
            const isChecked = linkedIds.has(book.id);
            const label = document.createElement('label');
    label.innerHTML = `<input type="checkbox" class="wb-book-checkbox" value="${book.id}" data-parent-category="${category.id}" ${isChecked ? 'checked' : ''}> ${book.name}`;
    bookContainer.appendChild(label);
});

        // --- ★ 核心修改 #1 在这里 ★ ---
        // 默认将分类设置为折叠状态
        header.classList.add('collapsed');
        bookContainer.classList.add('collapsed');
        // --- ★ 修改结束 ★ ---

        worldBookCheckboxesContainer.appendChild(header);
        worldBookCheckboxesContainer.appendChild(bookContainer);
    }
});

updateWorldBookSelectionDisplay(); // 更新顶部的已选数量显示

// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 在 updateWorldBookSelectionDisplay(); 的下一行，粘贴这整块新代码 ▼▼▼

// 使用事件委托来处理所有点击和勾选事件，效率更高
worldBookCheckboxesContainer.addEventListener('click', (e) => {
    const header = e.target.closest('.wb-category-header');
    if (header && !e.target.matches('input[type="checkbox"]')) {
        const categoryId = header.querySelector('.wb-category-checkbox')?.dataset.categoryId;
        // 【修改】现在 categoryId 可能是数字，也可能是 "uncategorized" 字符串，所以这个判断能通过了！
        if (categoryId) { // <-- 把原来的 !categoryId return; 改成这样
            const bookContainer = worldBookCheckboxesContainer.querySelector(`.wb-book-container[data-container-for="${categoryId}"]`);
            if (bookContainer) {
                header.classList.toggle('collapsed');
                bookContainer.classList.toggle('collapsed');
            }
        }
    }
});

worldBookCheckboxesContainer.addEventListener('change', (e) => {
    const target = e.target;
    
    // 如果点击的是分类的“全选”复选框
    if (target.classList.contains('wb-category-checkbox')) {
        const categoryId = target.dataset.categoryId;
        const isChecked = target.checked;
        // 找到这个分类下的所有书籍复选框，并将它们的状态设置为与分类复选框一致
        const bookCheckboxes = worldBookCheckboxesContainer.querySelectorAll(`input.wb-book-checkbox[data-parent-category="${categoryId}"]`);
        bookCheckboxes.forEach(cb => cb.checked = isChecked);
    }
    
    // 如果点击的是单个书籍的复选框
    if (target.classList.contains('wb-book-checkbox')) {
        const categoryId = target.dataset.parentCategory;
        if (categoryId) { // 检查它是否属于一个分类
            const categoryCheckbox = worldBookCheckboxesContainer.querySelector(`input.wb-category-checkbox[data-category-id="${categoryId}"]`);
            const allBookCheckboxes = worldBookCheckboxesContainer.querySelectorAll(`input.wb-book-checkbox[data-parent-category="${categoryId}"]`);
            // 检查该分类下是否所有书籍都被选中了
            const allChecked = Array.from(allBookCheckboxes).every(cb => cb.checked);
            // 同步分类“全选”复选框的状态
            categoryCheckbox.checked = allChecked;
        }
    }
    
    // 每次变更后都更新顶部的已选数量显示
    updateWorldBookSelectionDisplay();
});

// ▲▲▲ 粘贴结束 ▲▲▲

    // 加载并更新所有预览相关控件
    const themeRadio = document.querySelector(`input[name="theme-select"][value="${chat.settings.theme || 'default'}"]`);
    if (themeRadio) themeRadio.checked = true;
    const fontSizeSlider = document.getElementById('font-size-slider');
    fontSizeSlider.value = chat.settings.fontSize || 13;
    document.getElementById('font-size-value').textContent = `${fontSizeSlider.value}px`;
    const customCssInput = document.getElementById('custom-css-input');
    customCssInput.value = chat.settings.customCss || '';
    
    updateSettingsPreview(); 

// 【这三行就是我们新加的，现在已经放在正确的位置了】
renderBubblePresetSelector();
document.getElementById('bubble-style-preset-select').addEventListener('change', handlePresetSelectChange);
document.getElementById('manage-bubble-presets-btn').addEventListener('click', openBubblePresetManager);
    document.getElementById('chat-settings-modal').classList.add('visible');
});
// ▲▲▲ 替换结束 ▲▲▲
            
function renderGroupMemberSettings(members) { 
    const container = document.getElementById('group-members-settings'); 
    container.innerHTML = ''; 
    members.forEach(member => { 
        const div = document.createElement('div'); 
        div.className = 'member-editor'; 
        div.dataset.memberId = member.id; 
        // ★★★【核心重构】★★★
        // 显示的是 groupNickname
        div.innerHTML = `<img src="${member.avatar}" alt="${member.groupNickname}"><div class="member-name">${member.groupNickname}</div>`; 
        div.addEventListener('click', () => openMemberEditor(member.id)); 
        container.appendChild(div); 
    }); 
}

function openMemberEditor(memberId) { 
    editingMemberId = memberId; 
    const chat = state.chats[state.activeChatId]; 
    const member = chat.members.find(m => m.id === memberId); 
    document.getElementById('member-name-input').value = member.groupNickname; 
    document.getElementById('member-persona-input').value = member.persona; 
    document.getElementById('member-avatar-preview').src = member.avatar; 
    document.getElementById('member-settings-modal').classList.add('visible'); 
}
            document.getElementById('cancel-member-settings-btn').addEventListener('click', () => { document.getElementById('member-settings-modal').classList.remove('visible'); editingMemberId = null; });
            document.getElementById('save-member-settings-btn').addEventListener('click', () => { 
    if (!editingMemberId) return; 
    const chat = state.chats[state.activeChatId]; 
    const member = chat.members.find(m => m.id === editingMemberId); 
    
    // ★★★【核心重构】★★★
    const newNickname = document.getElementById('member-name-input').value.trim();
    if (!newNickname) {
        alert("群昵称不能为空！");
        return;
    }
    member.groupNickname = newNickname; // 只修改群昵称
    member.persona = document.getElementById('member-persona-input').value; 
    member.avatar = document.getElementById('member-avatar-preview').src; 
    
    renderGroupMemberSettings(chat.members); 
    document.getElementById('member-settings-modal').classList.remove('visible'); 
});
            document.getElementById('reset-theme-btn').addEventListener('click', () => { document.getElementById('theme-default').checked = true; });
            document.getElementById('cancel-chat-settings-btn').addEventListener('click', () => { chatSettingsModal.classList.remove('visible'); });

document.getElementById('save-chat-settings-btn').addEventListener('click', async () => {
    if (!state.activeChatId) return;
    const chat = state.chats[state.activeChatId];
    const newName = document.getElementById('chat-name-input').value.trim();
    if (!newName) return alert('备注名/群名不能为空！');
    chat.name = newName;
    const selectedThemeRadio = document.querySelector('input[name="theme-select"]:checked');
    chat.settings.theme = selectedThemeRadio ? selectedThemeRadio.value : 'default';

    chat.settings.fontSize = parseInt(document.getElementById('font-size-slider').value);
    chat.settings.customCss = document.getElementById('custom-css-input').value.trim();

    chat.settings.myPersona = document.getElementById('my-persona').value;
    chat.settings.myAvatar = document.getElementById('my-avatar-preview').src;
const checkedBooks = document.querySelectorAll('#world-book-checkboxes-container input.wb-book-checkbox:checked');
    chat.settings.linkedWorldBookIds = Array.from(checkedBooks).map(cb => cb.value);

    if (chat.isGroup) {
        chat.settings.myNickname = document.getElementById('my-group-nickname-input').value.trim();
        chat.settings.groupAvatar = document.getElementById('group-avatar-preview').src;
    } else {
        chat.settings.aiPersona = document.getElementById('ai-persona').value;
        chat.settings.aiAvatar = document.getElementById('ai-avatar-preview').src;
        const selectedGroupId = document.getElementById('assign-group-select').value;
        chat.groupId = selectedGroupId ? parseInt(selectedGroupId) : null;
    }

    chat.settings.maxMemory = parseInt(document.getElementById('max-memory').value) || 10;
    await db.chats.put(chat);

    applyScopedCss(chat.settings.customCss, '#chat-messages', 'custom-bubble-style');
    
    chatSettingsModal.classList.remove('visible');
    renderChatInterface(state.activeChatId);
    renderChatList();
});
            document.getElementById('clear-chat-btn').addEventListener('click', async () => { if (!state.activeChatId) return; const chat = state.chats[state.activeChatId]; const confirmed = await showCustomConfirm('清空聊天记录', '此操作将永久删除此聊天的所有消息，无法恢复。确定要清空吗？', { confirmButtonClass: 'btn-danger' }); if (confirmed) { chat.history = []; await db.chats.put(chat); renderChatInterface(state.activeChatId); renderChatList(); chatSettingsModal.classList.remove('visible'); } });
            
            const setupFileUpload = (inputId, callback) => { document.getElementById(inputId).addEventListener('change', async (event) => { const file = event.target.files[0]; if (file) { const dataUrl = await new Promise((res, rej) => { const reader = new FileReader(); reader.onload = () => res(reader.result); reader.onerror = () => rej(reader.error); reader.readAsDataURL(file); }); callback(dataUrl); event.target.value = null; } }); };
            setupFileUpload('ai-avatar-input', (base64) => document.getElementById('ai-avatar-preview').src = base64);
            setupFileUpload('my-avatar-input', (base64) => document.getElementById('my-avatar-preview').src = base64);
            setupFileUpload('group-avatar-input', (base64) => document.getElementById('group-avatar-preview').src = base64);
            setupFileUpload('member-avatar-input', (base64) => document.getElementById('member-avatar-preview').src = base64);
            setupFileUpload('bg-input', (base64) => { if(state.activeChatId) { state.chats[state.activeChatId].settings.background = base64; const bgPreview = document.getElementById('bg-preview'); bgPreview.src = base64; bgPreview.style.display = 'block'; document.getElementById('remove-bg-btn').style.display = 'inline-block'; } });
            setupFileUpload('preset-avatar-input', (base64) => document.getElementById('preset-avatar-preview').src = base64);
            document.getElementById('remove-bg-btn').addEventListener('click', () => { if (state.activeChatId) { state.chats[state.activeChatId].settings.background = ''; const bgPreview = document.getElementById('bg-preview'); bgPreview.src = ''; bgPreview.style.display = 'none'; document.getElementById('remove-bg-btn').style.display = 'none'; } });

            const stickerPanel = document.getElementById('sticker-panel');
            document.getElementById('open-sticker-panel-btn').addEventListener('click', () => { renderStickerPanel(); stickerPanel.classList.add('visible'); });
            document.getElementById('close-sticker-panel-btn').addEventListener('click', () => stickerPanel.classList.remove('visible'));
            // ▼▼▼ 将原来的 add-sticker-btn 事件监听器替换为下面这行 ▼▼▼
document.getElementById('add-sticker-btn').addEventListener('click', openBulkAddStickersModal);
// ▲▲▲ 替换结束 ▲▲▲
            document.getElementById('upload-sticker-btn').addEventListener('click', () => document.getElementById('sticker-upload-input').click());

            // ▼▼▼ 用下面这段【支持多选】的代码，替换旧的 sticker-upload-input 事件监听器 ▼▼▼
document.getElementById('sticker-upload-input').addEventListener('change', async (event) => {
    const files = event.target.files;
    if (!files.length) return;

    const newStickers = [];
    let canceled = false;

    // 使用 for...of 循环来逐个处理选中的文件
    for (const file of files) {
        if (canceled) break; // 如果用户中途取消了，就跳出循环

        // 为每个文件生成一个临时的本地预览URL
        const previewUrl = URL.createObjectURL(file);
        
        // 弹出带图片预览的命名框
        const name = await showCustomPrompt(
            `为表情命名 (${newStickers.length + 1}/${files.length})`,
            "请输入表情名称",
            file.name.replace(/\.[^/.]+$/, ""), // 默认使用文件名作为名字
            'text',
            // 这是 showCustomPrompt 的一个隐藏功能，可以插入额外的HTML
            `<img src="${previewUrl}" style="max-width: 100px; max-height: 100px; margin-bottom: 10px; border-radius: 8px;">`
        );
        
        // 释放临时的预览URL，避免内存泄漏
        URL.revokeObjectURL(previewUrl);
        
        if (name && name.trim()) {
            // 用户确认命名，读取文件内容并准备保存
            const base64Url = await new Promise(resolve => {
                const reader = new FileReader();
                reader.onload = e => resolve(e.target.result);
                reader.readAsDataURL(file);
            });
            
            newStickers.push({
                id: 'sticker_' + (Date.now() + newStickers.length),
                url: base64Url,
                name: name.trim()
            });
        } else if (name === null) {
            // 如果用户点击了“取消”
            const confirmCancel = await showCustomConfirm("确认取消", "确定要取消剩余表情的上传吗？");
            if (confirmCancel) {
                canceled = true;
            }
        } else {
            alert("表情名不能为空！");
        }
    }
    
    // 循环结束后，如果收集到了新表情，就批量添加到数据库
    if (newStickers.length > 0) {
        await db.userStickers.bulkAdd(newStickers);
        state.userStickers.push(...newStickers);
        renderStickerPanel();
        await showCustomAlert("上传成功", `已成功添加 ${newStickers.length} 个新表情！`);
    }

    // 清空文件选择器的值，以便下次能选择相同的文件
    event.target.value = null;
});
// ▲▲▲ 替换结束 ▲▲▲

            document.getElementById('upload-image-btn').addEventListener('click', () => document.getElementById('image-upload-input').click());
            document.getElementById('image-upload-input').addEventListener('change', async (event) => { const file = event.target.files[0]; if (!file || !state.activeChatId) return; const reader = new FileReader(); reader.onload = async (e) => { const base64Url = e.target.result; const chat = state.chats[state.activeChatId]; const msg = { role: 'user', content: [{ type: 'image_url', image_url: { url: base64Url } }], timestamp: Date.now() }; chat.history.push(msg); await db.chats.put(chat); appendMessage(msg, chat); renderChatList(); }; reader.readAsDataURL(file); event.target.value = null; });
            document.getElementById('voice-message-btn').addEventListener('click', async () => { if (!state.activeChatId) return; const text = await showCustomPrompt("发送语音", "请输入你想说的内容："); if (text && text.trim()) { const chat = state.chats[state.activeChatId]; const msg = { role: 'user', type: 'voice_message', content: text.trim(), timestamp: Date.now() }; chat.history.push(msg); await db.chats.put(chat); appendMessage(msg, chat); renderChatList(); } });
            document.getElementById('send-photo-btn').addEventListener('click', async () => { if (!state.activeChatId) return; const description = await showCustomPrompt("发送照片", "请用文字描述您要发送的照片："); if (description && description.trim()) { const chat = state.chats[state.activeChatId]; const msg = { role: 'user', type: 'user_photo', content: description.trim(), timestamp: Date.now() }; chat.history.push(msg); await db.chats.put(chat); appendMessage(msg, chat); renderChatList(); } });

// ▼▼▼ 【全新】外卖请求功能事件绑定 ▼▼▼
const waimaiModal = document.getElementById('waimai-request-modal');
document.getElementById('send-waimai-request-btn').addEventListener('click', () => {
    waimaiModal.classList.add('visible');
});

document.getElementById('waimai-cancel-btn').addEventListener('click', () => {
    waimaiModal.classList.remove('visible');
});

document.getElementById('waimai-confirm-btn').addEventListener('click', async () => {
    if (!state.activeChatId) return;
    
    const productInfoInput = document.getElementById('waimai-product-info');
    const amountInput = document.getElementById('waimai-amount');
    
    const productInfo = productInfoInput.value.trim();
    const amount = parseFloat(amountInput.value);

    if (!productInfo) {
        alert('请输入商品信息！');
        return;
    }
    if (isNaN(amount) || amount <= 0) {
        alert('请输入有效的代付金额！');
        return;
    }

    const chat = state.chats[state.activeChatId];
    const now = Date.now();

    // 【核心修正】在这里获取用户自己的昵称
    const myNickname = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
    
    const msg = {
        role: 'user',
        // 【核心修正】将获取到的昵称，作为 senderName 添加到消息对象中
        senderName: myNickname, 
        type: 'waimai_request',
        productInfo: productInfo,
        amount: amount,
        status: 'pending',
        countdownEndTime: now + 15 * 60 * 1000,
        timestamp: now
    };

    chat.history.push(msg);
    await db.chats.put(chat);
    appendMessage(msg, chat);
    renderChatList();

    productInfoInput.value = '';
    amountInput.value = '';
    waimaiModal.classList.remove('visible');
});         
            document.getElementById('open-persona-library-btn').addEventListener('click', openPersonaLibrary);
            document.getElementById('close-persona-library-btn').addEventListener('click', closePersonaLibrary);
            document.getElementById('add-persona-preset-btn').addEventListener('click', openPersonaEditorForCreate);
            document.getElementById('cancel-persona-editor-btn').addEventListener('click', closePersonaEditor);
            document.getElementById('save-persona-preset-btn').addEventListener('click', savePersonaPreset);
            document.getElementById('preset-action-edit').addEventListener('click', openPersonaEditorForEdit);
            document.getElementById('preset-action-delete').addEventListener('click', deletePersonaPreset);
            document.getElementById('preset-action-cancel').addEventListener('click', hidePresetActions);
            
            document.getElementById('selection-cancel-btn').addEventListener('click', exitSelectionMode);

// ▼▼▼ 【最终加强版】用这块代码替换旧的 selection-delete-btn 事件监听器 ▼▼▼
document.getElementById('selection-delete-btn').addEventListener('click', async () => {
    if (selectedMessages.size === 0) return;
    const confirmed = await showCustomConfirm('删除消息', `确定要删除选中的 ${selectedMessages.size} 条消息吗？这将改变AI的记忆。`, { confirmButtonClass: 'btn-danger' });
    if (confirmed) {
        const chat = state.chats[state.activeChatId];
        
        // 1. 【核心加强】在删除前，检查被删除的消息中是否包含投票
        let deletedPollsInfo = [];
        for (const timestamp of selectedMessages) {
            const msg = chat.history.find(m => m.timestamp === timestamp);
            if (msg && msg.type === 'poll') {
                deletedPollsInfo.push(`关于“${msg.question}”的投票(时间戳: ${msg.timestamp})`);
            }
        }
        
        // 2. 更新后端的历史记录
        chat.history = chat.history.filter(msg => !selectedMessages.has(msg.timestamp));
        
        // 3. 【核心加强】构建更具体的“遗忘指令”
        let forgetReason = "一些之前的消息已被用户删除。";
        if (deletedPollsInfo.length > 0) {
            forgetReason += ` 其中包括以下投票：${deletedPollsInfo.join('；')}。`;
        }
        forgetReason += " 你应该像它们从未存在过一样继续对话，并相应地调整你的记忆和行为，不要再提及这些被删除的内容。";

        const forgetInstruction = {
            role: 'system',
            content: `[系统提示：${forgetReason}]`,
            timestamp: Date.now(),
            isHidden: true 
        };
        chat.history.push(forgetInstruction);
        
        // 4. 将包含“遗忘指令”的、更新后的chat对象存回数据库
        await db.chats.put(chat);
        
        // 5. 最后才更新UI
        renderChatInterface(state.activeChatId);
        renderChatList();
    }
});
// ▲▲▲ 替换结束 ▲▲▲

            const fontUrlInput = document.getElementById('font-url-input');
            fontUrlInput.addEventListener('input', () => applyCustomFont(fontUrlInput.value.trim(), true));
            document.getElementById('save-font-btn').addEventListener('click', async () => {
                const newFontUrl = fontUrlInput.value.trim();
                if (!newFontUrl) { alert("请输入有效的字体URL。"); return; }
                applyCustomFont(newFontUrl, false);
                state.globalSettings.fontUrl = newFontUrl;
                await db.globalSettings.put(state.globalSettings);
                alert('字体已保存并应用！');
            });
            document.getElementById('reset-font-btn').addEventListener('click', resetToDefaultFont);

            document.querySelectorAll('#chat-list-bottom-nav .nav-item').forEach(item => { item.addEventListener('click', () => switchToChatListView(item.dataset.view)); });
            document.getElementById('qzone-back-btn').addEventListener('click', () => switchToChatListView('messages-view'));
            document.getElementById('qzone-nickname').addEventListener('click', async () => { const newNickname = await showCustomPrompt("修改昵称", "请输入新的昵称", state.qzoneSettings.nickname); if (newNickname && newNickname.trim()) { state.qzoneSettings.nickname = newNickname.trim(); await saveQzoneSettings(); renderQzoneScreen(); } });
            document.getElementById('qzone-avatar-container').addEventListener('click', () => document.getElementById('qzone-avatar-input').click());
            document.getElementById('qzone-banner-container').addEventListener('click', () => document.getElementById('qzone-banner-input').click());
            document.getElementById('qzone-avatar-input').addEventListener('change', async (event) => { const file = event.target.files[0]; if (file) { const dataUrl = await new Promise(res => { const reader = new FileReader(); reader.onload = () => res(reader.result); reader.readAsDataURL(file); }); state.qzoneSettings.avatar = dataUrl; await saveQzoneSettings(); renderQzoneScreen(); } event.target.value = null; });
            document.getElementById('qzone-banner-input').addEventListener('change', async (event) => { const file = event.target.files[0]; if (file) { const dataUrl = await new Promise(res => { const reader = new FileReader(); reader.onload = () => res(reader.result); reader.readAsDataURL(file); }); state.qzoneSettings.banner = dataUrl; await saveQzoneSettings(); renderQzoneScreen(); } event.target.value = null; });

// ▼▼▼ 【修正后】的“说说”按钮事件 ▼▼▼
document.getElementById('create-shuoshuo-btn').addEventListener('click', async () => {
    // 1. 重置并获取模态框
    resetCreatePostModal();
    const modal = document.getElementById('create-post-modal');
    
    // 2. 设置为“说说”模式
    modal.dataset.mode = 'shuoshuo';
    
    // 3. 隐藏与图片/文字图相关的部分
    modal.querySelector('.post-mode-switcher').style.display = 'none';
    modal.querySelector('#image-mode-content').style.display = 'none';
    modal.querySelector('#text-image-mode-content').style.display = 'none';
    
    // 4. 修改主输入框的提示语，使其更符合“说说”的场景
    modal.querySelector('#post-public-text').placeholder = '分享新鲜事...';
    
    // 5. 准备并显示模态框
    const visibilityGroupsContainer = document.getElementById('post-visibility-groups');
    visibilityGroupsContainer.innerHTML = '';
    const groups = await db.qzoneGroups.toArray();
    if (groups.length > 0) {
        groups.forEach(group => {
            const label = document.createElement('label');
            label.style.display = 'block';
            label.innerHTML = `<input type="checkbox" name="visibility_group" value="${group.id}"> ${group.name}`;
            visibilityGroupsContainer.appendChild(label);
        });
    } else {
        visibilityGroupsContainer.innerHTML = '<p style="color: var(--text-secondary);">没有可用的分组</p>';
    }
    modal.classList.add('visible');
});

// ▼▼▼ 【修正后】的“动态”（图片）按钮事件 ▼▼▼
document.getElementById('create-post-btn').addEventListener('click', async () => {
    // 1. 重置并获取模态框
    resetCreatePostModal();
    const modal = document.getElementById('create-post-modal');
    
    // 2. 设置为“复杂动态”模式
    modal.dataset.mode = 'complex';
    
// 3. 确保与图片/文字图相关的部分是可见的
modal.querySelector('.post-mode-switcher').style.display = 'flex';
// 显式激活“上传图片”模式...
modal.querySelector('#image-mode-content').classList.add('active');
// ...同时确保“文字图”模式是隐藏的
modal.querySelector('#text-image-mode-content').classList.remove('active');
    
    // 4. 恢复主输入框的默认提示语
    modal.querySelector('#post-public-text').placeholder = '分享新鲜事...（非必填的公开文字）';

    // 5. 准备并显示模态框（与“说说”按钮的逻辑相同）
    const visibilityGroupsContainer = document.getElementById('post-visibility-groups');
    visibilityGroupsContainer.innerHTML = '';
    const groups = await db.qzoneGroups.toArray();
    if (groups.length > 0) {
        groups.forEach(group => {
            const label = document.createElement('label');
            label.style.display = 'block';
            label.innerHTML = `<input type="checkbox" name="visibility_group" value="${group.id}"> ${group.name}`;
            visibilityGroupsContainer.appendChild(label);
        });
    } else {
        visibilityGroupsContainer.innerHTML = '<p style="color: var(--text-secondary);">没有可用的分组</p>';
    }
    modal.classList.add('visible');
});
            document.getElementById('open-album-btn').addEventListener('click', async () => { await renderAlbumList(); showScreen('album-screen'); });
            document.getElementById('album-back-btn').addEventListener('click', () => { showScreen('chat-list-screen'); switchToChatListView('qzone-screen'); });

// --- ↓↓↓ 从这里开始复制 ↓↓↓ ---

document.getElementById('album-photos-back-btn').addEventListener('click', () => {
    state.activeAlbumId = null;
    showScreen('album-screen');
});

document.getElementById('album-upload-photo-btn').addEventListener('click', () => document.getElementById('album-photo-input').click());

document.getElementById('album-photo-input').addEventListener('change', async (event) => {
    if (!state.activeAlbumId) return;
    const files = event.target.files;
    if (!files.length) return;

    const album = await db.qzoneAlbums.get(state.activeAlbumId);
    
    for (const file of files) {
        const dataUrl = await new Promise(resolve => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result);
            reader.readAsDataURL(file);
        });
        await db.qzonePhotos.add({ albumId: state.activeAlbumId, url: dataUrl, createdAt: Date.now() });
    }

    const photoCount = await db.qzonePhotos.where('albumId').equals(state.activeAlbumId).count();
    const updateData = { photoCount };
    
    if (!album.photoCount || album.coverUrl.includes('placeholder')) {
        const firstPhoto = await db.qzonePhotos.where('albumId').equals(state.activeAlbumId).first();
        if(firstPhoto) updateData.coverUrl = firstPhoto.url;
    }

    await db.qzoneAlbums.update(state.activeAlbumId, updateData);
    await renderAlbumPhotosScreen();
    await renderAlbumList();
    
    event.target.value = null;
    alert('照片上传成功！');
});

// --- ↑↑↑ 复制到这里结束 ↑↑↑ ---

// --- ↓↓↓ 从这里开始复制，完整替换掉旧的 photos-grid-page 监听器 ↓↓↓ ---

document.getElementById('photos-grid-page').addEventListener('click', async (e) => {
    const deleteBtn = e.target.closest('.photo-delete-btn');
    const photoThumb = e.target.closest('.photo-thumb');

    if (deleteBtn) {
        e.stopPropagation(); // 阻止事件冒泡到图片上
        const photoId = parseInt(deleteBtn.dataset.photoId);
        const confirmed = await showCustomConfirm(
            '删除照片',
            '确定要删除这张照片吗？此操作不可恢复。',
            { confirmButtonClass: 'btn-danger' }
        );

        if (confirmed) {
            const deletedPhoto = await db.qzonePhotos.get(photoId);
            if (!deletedPhoto) return;
            
            await db.qzonePhotos.delete(photoId);

            const album = await db.qzoneAlbums.get(state.activeAlbumId);
            const photoCount = (album.photoCount || 1) - 1;
            const updateData = { photoCount };
            
            if (album.coverUrl === deletedPhoto.url) {
                const nextPhoto = await db.qzonePhotos.where('albumId').equals(state.activeAlbumId).first();
                updateData.coverUrl = nextPhoto ? nextPhoto.url : 'https://i.postimg.cc/pT2xKzPz/album-cover-placeholder.png';
            }
            
            await db.qzoneAlbums.update(state.activeAlbumId, updateData);
            await renderAlbumPhotosScreen();
            await renderAlbumList();
            alert('照片已删除。');
        }
    } 
    else if (photoThumb) {
        // 这就是恢复的图片点击放大功能！
        openPhotoViewer(photoThumb.src);
    }
});

// 恢复图片查看器的控制事件
document.getElementById('photo-viewer-close-btn').addEventListener('click', closePhotoViewer);
document.getElementById('photo-viewer-next-btn').addEventListener('click', showNextPhoto);
document.getElementById('photo-viewer-prev-btn').addEventListener('click', showPrevPhoto);

// 恢复键盘左右箭头和ESC键的功能
document.addEventListener('keydown', (e) => {
    if (!photoViewerState.isOpen) return; 

    if (e.key === 'ArrowRight') {
        showNextPhoto();
    } else if (e.key === 'ArrowLeft') {
        showPrevPhoto();
    } else if (e.key === 'Escape') {
        closePhotoViewer();
    }
});

// --- ↑↑↑ 复制到这里结束 ↑↑↑ ---
         
document.getElementById('create-album-btn-page').addEventListener('click', async () => { const albumName = await showCustomPrompt("创建新相册", "请输入相册名称"); if (albumName && albumName.trim()) { const newAlbum = { name: albumName.trim(), coverUrl: 'https://i.postimg.cc/pT2xKzPz/album-cover-placeholder.png', photoCount: 0, createdAt: Date.now() }; await db.qzoneAlbums.add(newAlbum); await renderAlbumList(); alert(`相册 "${albumName}" 创建成功！`); } else if (albumName !== null) { alert("相册名称不能为空！"); } });

            document.getElementById('cancel-create-post-btn').addEventListener('click', () => document.getElementById('create-post-modal').classList.remove('visible'));
            document.getElementById('post-upload-local-btn').addEventListener('click', () => document.getElementById('post-local-image-input').click());
            document.getElementById('post-local-image-input').addEventListener('change', (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { document.getElementById('post-image-preview').src = e.target.result; document.getElementById('post-image-preview-container').classList.add('visible'); document.getElementById('post-image-desc-group').style.display = 'block'; }; reader.readAsDataURL(file); } });
            document.getElementById('post-use-url-btn').addEventListener('click', async () => { const url = await showCustomPrompt("输入图片URL", "请输入网络图片的链接", "", "url"); if (url) { document.getElementById('post-image-preview').src = url; document.getElementById('post-image-preview-container').classList.add('visible'); document.getElementById('post-image-desc-group').style.display = 'block'; } });
            document.getElementById('post-remove-image-btn').addEventListener('click', () => resetCreatePostModal());
            const imageModeBtn = document.getElementById('switch-to-image-mode');
            const textImageModeBtn = document.getElementById('switch-to-text-image-mode');
            const imageModeContent = document.getElementById('image-mode-content');
            const textImageModeContent = document.getElementById('text-image-mode-content');
            imageModeBtn.addEventListener('click', () => { imageModeBtn.classList.add('active'); textImageModeBtn.classList.remove('active'); imageModeContent.classList.add('active'); textImageModeContent.classList.remove('active'); });
            textImageModeBtn.addEventListener('click', () => { textImageModeBtn.classList.add('active'); imageModeBtn.classList.remove('active'); textImageModeContent.classList.add('active'); imageModeContent.classList.remove('active'); });

// ▼▼▼ 【最终修正版】的“发布”按钮事件，已修复权限漏洞 ▼▼▼
document.getElementById('confirm-create-post-btn').addEventListener('click', async () => {
    const modal = document.getElementById('create-post-modal');
    const mode = modal.dataset.mode;
    
    // --- 1. 获取通用的可见性设置 ---
    const visibilityMode = document.querySelector('input[name="visibility"]:checked').value;
    let visibleGroupIds = null;
    
    if (visibilityMode === 'include') {
        visibleGroupIds = Array.from(document.querySelectorAll('input[name="visibility_group"]:checked')).map(cb => parseInt(cb.value));
    }

    let newPost = {};
    const basePostData = {
        timestamp: Date.now(),
        authorId: 'user',
        // 【重要】在这里就把权限信息存好
        visibleGroupIds: visibleGroupIds,
    };

    // --- 2. 根据模式构建不同的 post 对象 ---
    if (mode === 'shuoshuo') {
        const content = document.getElementById('post-public-text').value.trim();
        if (!content) {
            alert('说说内容不能为空哦！');
            return;
        }
        newPost = {
            ...basePostData,
            type: 'shuoshuo',
            content: content,
        };

    } else { // 处理 'complex' 模式 (图片/文字图)
        const publicText = document.getElementById('post-public-text').value.trim();
        const isImageModeActive = document.getElementById('image-mode-content').classList.contains('active');

        if (isImageModeActive) {
            const imageUrl = document.getElementById('post-image-preview').src;
            const imageDescription = document.getElementById('post-image-description').value.trim();
            if (!imageUrl || !(imageUrl.startsWith('http') || imageUrl.startsWith('data:'))) {
                alert('请先添加一张图片再发布动态哦！');
                return;
            }
            if (!imageDescription) {
                alert('请为你的图片添加一个简单的描述（必填，给AI看的）！');
                return;
            }
            newPost = {
                ...basePostData,
                type: 'image_post',
                publicText: publicText,
                imageUrl: imageUrl,
                imageDescription: imageDescription,
            };
        } else { // 文字图模式
            const hiddenText = document.getElementById('post-hidden-text').value.trim();
            if (!hiddenText) {
                alert('请输入文字图描述！');
                return;
            }
            newPost = {
                ...basePostData,
                type: 'text_image',
                publicText: publicText,
                hiddenContent: hiddenText,
            };
        }
    }

    // --- 3. 保存到数据库 ---
    const newPostId = await db.qzonePosts.add(newPost);
    let postSummary = newPost.content || newPost.publicText || newPost.imageDescription || newPost.hiddenContent || "（无文字内容）";
    postSummary = postSummary.substring(0, 50) + (postSummary.length > 50 ? '...' : '');

    // --- 4. 【核心修正】带有权限检查的通知循环 ---
    for (const chatId in state.chats) {
        const chat = state.chats[chatId];
        if (chat.isGroup) continue; // 跳过群聊

        let shouldNotify = false;
        const postVisibleGroups = newPost.visibleGroupIds;

        // 判断条件1：如果动态是公开的 (没有设置任何可见分组)
        if (!postVisibleGroups || postVisibleGroups.length === 0) {
            shouldNotify = true;
        } 
        // 判断条件2：如果动态设置了部分可见，并且当前角色在可见分组内
        else if (chat.groupId && postVisibleGroups.includes(chat.groupId)) {
            shouldNotify = true;
        }

        // 只有满足条件的角色才会被通知
        if (shouldNotify) {
            const historyMessage = {
                role: 'system',
                content: `[系统提示：用户刚刚发布了一条动态(ID: ${newPostId})，内容摘要是：“${postSummary}”。你现在可以对这条动态进行评论了。]`,
                timestamp: Date.now(),
                isHidden: true
            };
            chat.history.push(historyMessage);
            await db.chats.put(chat);
        }
    }
    // --- 修正结束 ---

    await renderQzonePosts();
    modal.classList.remove('visible');
    alert('动态发布成功！');
});

// ▼▼▼ 请用这【一整块】包含所有滑动和点击事件的完整代码，替换掉旧的 postsList 事件监听器 ▼▼▼

const postsList = document.getElementById('qzone-posts-list');
let swipeState = { isDragging: false, startX: 0, startY: 0, currentX: 0, activeContainer: null, swipeDirection: null, isClick: true };

function resetAllSwipes(exceptThisOne = null) {
    document.querySelectorAll('.qzone-post-container').forEach(container => {
        if (container !== exceptThisOne) {
            container.querySelector('.qzone-post-item').classList.remove('swiped');
        }
    });
}

const handleSwipeStart = (e) => {
    const targetContainer = e.target.closest('.qzone-post-container');
    if (!targetContainer) return;

    resetAllSwipes(targetContainer);
    swipeState.activeContainer = targetContainer;
    swipeState.isDragging = true;
    swipeState.isClick = true;
    swipeState.swipeDirection = null;
    swipeState.startX = e.type.includes('mouse') ? e.pageX : e.touches[0].pageX;
    swipeState.startY = e.type.includes('mouse') ? e.pageY : e.touches[0].pageY;
    swipeState.activeContainer.querySelector('.qzone-post-item').style.transition = 'none';
};

const handleSwipeMove = (e) => {
    if (!swipeState.isDragging || !swipeState.activeContainer) return;

    const currentX = e.type.includes('mouse') ? e.pageX : e.touches[0].pageX;
    const currentY = e.type.includes('mouse') ? e.pageY : e.touches[0].pageY;
    const diffX = currentX - swipeState.startX;
    const diffY = currentY - swipeState.startY;
    const absDiffX = Math.abs(diffX);
    const absDiffY = Math.abs(diffY);
    const clickThreshold = 5;

    if (absDiffX > clickThreshold || absDiffY > clickThreshold) {
        swipeState.isClick = false;
    }

    if (swipeState.swipeDirection === null) {
        if (absDiffX > clickThreshold || absDiffY > clickThreshold) {
            if (absDiffX > absDiffY) {
                swipeState.swipeDirection = 'horizontal';
            } else {
                swipeState.swipeDirection = 'vertical';
            }
        }
    }
    if (swipeState.swipeDirection === 'vertical') {
        handleSwipeEnd(e);
        return;
    }
    if (swipeState.swipeDirection === 'horizontal') {
        e.preventDefault();
        swipeState.currentX = currentX;
        let translation = diffX;
        if (translation > 0) translation = 0;
        if (translation < -90) translation = -90;
        swipeState.activeContainer.querySelector('.qzone-post-item').style.transform = `translateX(${translation}px)`;
    }
};

const handleSwipeEnd = (e) => {
    if (swipeState.isClick) {
        swipeState.isDragging = false;
        swipeState.activeContainer = null;
        return;
    }
    if (!swipeState.isDragging || !swipeState.activeContainer) return;

    const postItem = swipeState.activeContainer.querySelector('.qzone-post-item');
    postItem.style.transition = 'transform 0.3s ease';

    const finalX = e.type.includes('touchend') ? e.changedTouches[0].pageX : e.pageX;
    const diffX = finalX - swipeState.startX;
    const swipeThreshold = -40;

    if (swipeState.swipeDirection === 'horizontal' && diffX < swipeThreshold) {
        postItem.classList.add('swiped');
        postItem.style.transform = '';
    } else {
        postItem.classList.remove('swiped');
        postItem.style.transform = '';
    }

    swipeState.isDragging = false;
    swipeState.startX = 0;
    swipeState.startY = 0;
    swipeState.currentX = 0;
    swipeState.activeContainer = null;
    swipeState.swipeDirection = null;
    swipeState.isClick = true;
};

// --- 绑定所有滑动事件 ---
postsList.addEventListener('mousedown', handleSwipeStart);
document.addEventListener('mousemove', handleSwipeMove);
document.addEventListener('mouseup', handleSwipeEnd);
postsList.addEventListener('touchstart', handleSwipeStart, { passive: false });
postsList.addEventListener('touchmove', handleSwipeMove, { passive: false });
postsList.addEventListener('touchend', handleSwipeEnd);

// --- 绑定所有点击事件 ---
postsList.addEventListener('click', async (e) => {
    e.stopPropagation();
    const target = e.target;

    // --- 新增：处理评论删除按钮 ---
    if (target.classList.contains('comment-delete-btn')) {
        const postContainer = target.closest('.qzone-post-container');
        if (!postContainer) return;

        const postId = parseInt(postContainer.dataset.postId);
        const commentIndex = parseInt(target.dataset.commentIndex);
        if (isNaN(postId) || isNaN(commentIndex)) return;

        const post = await db.qzonePosts.get(postId);
        if (!post || !post.comments || !post.comments[commentIndex]) return;

        const commentText = post.comments[commentIndex].text;
        const confirmed = await showCustomConfirm(
            '删除评论',
            `确定要删除这条评论吗？\n\n“${commentText.substring(0, 50)}...”`,
            { confirmButtonClass: 'btn-danger' }
        );

        if (confirmed) {
            // 从数组中移除该评论
            post.comments.splice(commentIndex, 1);
            // 更新数据库
            await db.qzonePosts.update(postId, { comments: post.comments });
            // 重新渲染列表以反映更改
            await renderQzonePosts();
            alert('评论已删除。');
        }
        return; // 处理完后直接返回
    }

    if (target.classList.contains('post-actions-btn')) {
        const container = target.closest('.qzone-post-container');
        if (container && container.dataset.postId) {
            showPostActions(parseInt(container.dataset.postId));
        }
        return;
    }

    if (target.closest('.qzone-post-delete-action')) {
        const container = target.closest('.qzone-post-container');
        if (!container) return;
        
        const postIdToDelete = parseInt(container.dataset.postId);
        if (isNaN(postIdToDelete)) return;

        const confirmed = await showCustomConfirm('删除动态', '确定要永久删除这条动态吗？', { confirmButtonClass: 'btn-danger' });

        if (confirmed) {
            container.style.transition = 'all 0.3s ease';
            container.style.transform = 'scale(0.8)';
            container.style.opacity = '0';
        
            setTimeout(async () => {
                 await db.qzonePosts.delete(postIdToDelete);
                 
                 const notificationIdentifier = `(ID: ${postIdToDelete})`;
                 for (const chatId in state.chats) {
                     const chat = state.chats[chatId];
                     const originalHistoryLength = chat.history.length;
                     chat.history = chat.history.filter(msg => !(msg.role === 'system' && msg.content.includes(notificationIdentifier)));
                     if (chat.history.length < originalHistoryLength) {
                         await db.chats.put(chat);
                     }
                 }
                 await renderQzonePosts();
                 alert('动态已删除。');
            }, 300);
        }
        return;
    }

    if (target.tagName === 'IMG' && target.dataset.hiddenText) {
        const hiddenText = target.dataset.hiddenText;
        showCustomAlert("图片内容", hiddenText.replace(/<br>/g, '\n'));
        return;
    }
    const icon = target.closest('.action-icon');
    if (icon) {
        const postContainer = icon.closest('.qzone-post-container');
        if (!postContainer) return;
        const postId = parseInt(postContainer.dataset.postId);
        if (isNaN(postId)) return;
        if (icon.classList.contains('like')) {
            const post = await db.qzonePosts.get(postId);
            if (!post) return;
            if (!post.likes) post.likes = [];
            const userNickname = state.qzoneSettings.nickname;
            const userLikeIndex = post.likes.indexOf(userNickname);
            if (userLikeIndex > -1) {
                post.likes.splice(userLikeIndex, 1);
            } else {
                post.likes.push(userNickname);
                icon.classList.add('animate-like');
                icon.addEventListener('animationend', () => icon.classList.remove('animate-like'), { once: true });
            }
            await db.qzonePosts.update(postId, { likes: post.likes });
        }
        if (icon.classList.contains('favorite')) {
            const existingFavorite = await db.favorites.where({ type: 'qzone_post', 'content.id': postId }).first();
            if (existingFavorite) {
                await db.favorites.delete(existingFavorite.id);
                await showCustomAlert('提示', '已取消收藏');
            } else {
                const postToSave = await db.qzonePosts.get(postId);
                if (postToSave) {
                    await db.favorites.add({ type: 'qzone_post', content: postToSave, timestamp: Date.now() });
                    await showCustomAlert('提示', '收藏成功！');
                }
            }
        }
        await renderQzonePosts();
        return;
    }
    const sendBtn = target.closest('.comment-send-btn');
    if (sendBtn) {
        const postContainer = sendBtn.closest('.qzone-post-container');
        if (!postContainer) return;
        const postId = parseInt(postContainer.dataset.postId);
        const commentInput = postContainer.querySelector('.comment-input');
        const commentText = commentInput.value.trim();
        if (!commentText) return alert('评论内容不能为空哦！');
        const post = await db.qzonePosts.get(postId);
        if (!post) return;
        if (!post.comments) post.comments = [];
        post.comments.push({ commenterName: state.qzoneSettings.nickname, text: commentText, timestamp: Date.now() });
        await db.qzonePosts.update(postId, { comments: post.comments });
        for (const chatId in state.chats) {
            const chat = state.chats[chatId];
            if (!chat.isGroup) {
                chat.history.push({ role: 'system', content: `[系统提示：'${state.qzoneSettings.nickname}' 在ID为${postId}的动态下发表了评论：“${commentText}”]`, timestamp: Date.now(), isHidden: true });
                await db.chats.put(chat);
            }
        }
        commentInput.value = '';
        await renderQzonePosts();
        return;
    }
});
// ▲▲▲ 替换结束 ▲▲▲

            // ▼▼▼ 在 init() 函数的事件监听器区域，粘贴下面这两行 ▼▼▼

            // 绑定动态页和收藏页的返回按钮
            document.getElementById('qzone-back-btn').addEventListener('click', () => switchToChatListView('messages-view'));
            document.getElementById('favorites-back-btn').addEventListener('click', () => switchToChatListView('messages-view'));

            // ▲▲▲ 添加结束 ▲▲▲

            // ▼▼▼ 在 init() 函数的事件监听器区域，检查并确保你有这段完整的代码 ▼▼▼

            // 收藏页搜索功能
            const searchInput = document.getElementById('favorites-search-input');
            const searchClearBtn = document.getElementById('favorites-search-clear-btn');

            searchInput.addEventListener('input', () => {
                const searchTerm = searchInput.value.trim().toLowerCase();
                
                // 控制清除按钮的显示/隐藏
                searchClearBtn.style.display = searchTerm ? 'block' : 'none';

                if (!searchTerm) {
                    displayFilteredFavorites(allFavoriteItems); // 如果搜索框为空，显示所有
                    return;
                }

                // 筛选逻辑
                const filteredItems = allFavoriteItems.filter(item => {
                    let contentToSearch = '';
                    let authorToSearch = '';

                    if (item.type === 'qzone_post') {
                        const post = item.content;
                        contentToSearch += (post.publicText || '') + ' ' + (post.content || '');
                        if (post.authorId === 'user') {
                            authorToSearch = state.qzoneSettings.nickname;
                        } else if (state.chats[post.authorId]) {
                            authorToSearch = state.chats[post.authorId].name;
                        }
                    } else if (item.type === 'chat_message') {
                        const msg = item.content;
                        if (typeof msg.content === 'string') {
                            contentToSearch = msg.content;
                        }
                        const chat = state.chats[item.chatId];
                        if (chat) {
                           if (msg.role === 'user') {
                                authorToSearch = chat.isGroup ? (chat.settings.myNickname || '我') : '我';
                           } else {
                                authorToSearch = chat.isGroup ? msg.senderName : chat.name;
                           }
                        }
                    }
                    
                    // 同时搜索内容和作者，并且不区分大小写
                    return contentToSearch.toLowerCase().includes(searchTerm) || 
                           authorToSearch.toLowerCase().includes(searchTerm);
                });

                displayFilteredFavorites(filteredItems);
            });

            // 清除按钮的点击事件
            searchClearBtn.addEventListener('click', () => {
                searchInput.value = '';
                searchClearBtn.style.display = 'none';
                displayFilteredFavorites(allFavoriteItems);
                searchInput.focus();
            });

            // ▲▲▲ 代码检查结束 ▲▲▲

            // ▼▼▼ 新增/修改的事件监听器 ▼▼▼
            
            // 为聊天界面的批量收藏按钮绑定事件
                        // 为聊天界面的批量收藏按钮绑定事件 (已修正)
            document.getElementById('selection-favorite-btn').addEventListener('click', async () => {
                if (selectedMessages.size === 0) return;
                const chat = state.chats[state.activeChatId];
                if (!chat) return;

                const favoritesToAdd = [];
                const timestampsToFavorite = [...selectedMessages];

                for (const timestamp of timestampsToFavorite) {
                    // 【核心修正1】使用新的、高效的索引进行查询
                    const existing = await db.favorites.where('originalTimestamp').equals(timestamp).first();
                    
                    if (!existing) {
                        const messageToSave = chat.history.find(msg => msg.timestamp === timestamp);
                        if (messageToSave) {
                            favoritesToAdd.push({
                                type: 'chat_message',
                                content: messageToSave,
                                chatId: state.activeChatId,
                                timestamp: Date.now(), // 这是收藏操作发生的时间
                                originalTimestamp: messageToSave.timestamp // 【核心修正2】保存原始消息的时间戳到新字段
                            });
                        }
                    }
                }

                if (favoritesToAdd.length > 0) {
                    await db.favorites.bulkAdd(favoritesToAdd);
                    allFavoriteItems = await db.favorites.orderBy('timestamp').reverse().toArray(); // 更新全局收藏缓存
                    await showCustomAlert('收藏成功', `已成功收藏 ${favoritesToAdd.length} 条消息。`);
                } else {
                    await showCustomAlert('提示', '选中的消息均已收藏过。');
                }
                
                exitSelectionMode();
            });

            // 收藏页面的"编辑"按钮事件 (已修正)
            const favoritesEditBtn = document.getElementById('favorites-edit-btn');
            const favoritesView = document.getElementById('favorites-view');
            const favoritesActionBar = document.getElementById('favorites-action-bar');
            const mainBottomNav = document.getElementById('chat-list-bottom-nav'); // 获取主导航栏
            const favoritesList = document.getElementById('favorites-list'); // 获取收藏列表
            
            favoritesEditBtn.addEventListener('click', () => {
                isFavoritesSelectionMode = !isFavoritesSelectionMode;
                favoritesView.classList.toggle('selection-mode', isFavoritesSelectionMode);

                if (isFavoritesSelectionMode) {
                    // --- 进入编辑模式 ---
                    favoritesEditBtn.textContent = '完成';
                    favoritesActionBar.style.display = 'block'; // 显示删除操作栏
                    mainBottomNav.style.display = 'none'; // ▼ 新增：隐藏主导航栏
                    favoritesList.style.paddingBottom = '80px'; // ▼ 新增：给列表底部增加空间
                } else {
                    // --- 退出编辑模式 ---
                    favoritesEditBtn.textContent = '编辑';
                    favoritesActionBar.style.display = 'none'; // 隐藏删除操作栏
                    mainBottomNav.style.display = 'flex';  // ▼ 新增：恢复主导航栏
                    favoritesList.style.paddingBottom = ''; // ▼ 新增：恢复列表默认padding

                    // 退出时清空所有选择
                    selectedFavorites.clear();
                    document.querySelectorAll('.favorite-item-card.selected').forEach(card => card.classList.remove('selected'));
                    document.getElementById('favorites-delete-selected-btn').textContent = `删除 (0)`;
                }
            });

// ▼▼▼ 将它【完整替换】为下面这段修正后的代码 ▼▼▼
// 收藏列表的点击选择事件 (事件委托)
document.getElementById('favorites-list').addEventListener('click', (e) => {
    const target = e.target;
    const card = target.closest('.favorite-item-card');

    // 【新增】处理文字图点击，这段逻辑要放在最前面，保证任何模式下都生效
    if (target.tagName === 'IMG' && target.dataset.hiddenText) {
        const hiddenText = target.dataset.hiddenText;
        showCustomAlert("图片内容", hiddenText.replace(/<br>/g, '\n'));
        return; // 处理完就退出，不继续执行选择逻辑
    }
    
    // 如果不在选择模式，则不执行后续的选择操作
    if (!isFavoritesSelectionMode) return;

    // --- 以下是原有的选择逻辑，保持不变 ---
    if (!card) return;

    const favId = parseInt(card.dataset.favid);
    if (isNaN(favId)) return;

    // 切换选择状态
    if (selectedFavorites.has(favId)) {
        selectedFavorites.delete(favId);
        card.classList.remove('selected');
    } else {
        selectedFavorites.add(favId);
        card.classList.add('selected');
    }
    
    // 更新底部删除按钮的计数
    document.getElementById('favorites-delete-selected-btn').textContent = `删除 (${selectedFavorites.size})`;
});

// ▼▼▼ 将它【完整替换】为下面这段修正后的代码 ▼▼▼
// 收藏页面批量删除按钮事件
document.getElementById('favorites-delete-selected-btn').addEventListener('click', async () => {
    if (selectedFavorites.size === 0) return;

    const confirmed = await showCustomConfirm(
        '确认删除', 
        `确定要从收藏夹中移除这 ${selectedFavorites.size} 条内容吗？`, 
        { confirmButtonClass: 'btn-danger' }
    );

    if (confirmed) {
        const idsToDelete = [...selectedFavorites];
        await db.favorites.bulkDelete(idsToDelete);
        await showCustomAlert('删除成功', '选中的收藏已被移除。');
        
        // 【核心修正1】从前端缓存中也移除被删除的项
        allFavoriteItems = allFavoriteItems.filter(item => !idsToDelete.includes(item.id));
        
        // 【核心修正2】使用更新后的缓存，立即重新渲染列表
        displayFilteredFavorites(allFavoriteItems);
        
        // 最后，再退出编辑模式
        favoritesEditBtn.click(); // 模拟点击"完成"按钮来退出编辑模式
    }
});

// ▼▼▼ 在 init() 函数末尾添加 ▼▼▼
if (state.globalSettings.enableBackgroundActivity) {
    startBackgroundSimulation();
    console.log("后台活动模拟已自动启动。");
}
// ▲▲▲ 添加结束 ▲▲▲

// ▼▼▼ 【这是最终的正确代码】请粘贴这段代码到 init() 的事件监听器区域末尾 ▼▼▼

// --- 统一处理所有影响预览的控件的事件 ---

// 1. 监听主题选择
document.querySelectorAll('input[name="theme-select"]').forEach(radio => {
    radio.addEventListener('change', updateSettingsPreview);
});

// 2. 监听字体大小滑块
const fontSizeSlider = document.getElementById('font-size-slider');
fontSizeSlider.addEventListener('input', () => {
    // a. 实时更新数值显示
    document.getElementById('font-size-value').textContent = `${fontSizeSlider.value}px`;
    // b. 更新预览
    updateSettingsPreview();
});

// 3. 监听自定义CSS输入框
const customCssInputForPreview = document.getElementById('custom-css-input');
customCssInputForPreview.addEventListener('input', updateSettingsPreview);

// 4. 监听重置按钮
document.getElementById('reset-theme-btn').addEventListener('click', () => {
    document.getElementById('theme-default').checked = true;
    updateSettingsPreview();
});

document.getElementById('reset-custom-css-btn').addEventListener('click', () => {
    document.getElementById('custom-css-input').value = '';
    updateSettingsPreview();
});

// ▲▲▲ 粘贴结束 ▲▲▲

// ▼▼▼ 请将这段【新代码】粘贴到 init() 的事件监听器区域末尾 ▼▼▼
document.querySelectorAll('input[name="visibility"]').forEach(radio => {
    radio.addEventListener('change', function() {
        const groupsContainer = document.getElementById('post-visibility-groups');
        if (this.value === 'include' || this.value === 'exclude') {
            groupsContainer.style.display = 'block';
        } else {
            groupsContainer.style.display = 'none';
        }
    });
});
// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 请将这段【新代码】粘贴到 init() 的事件监听器区域末尾 ▼▼▼
document.getElementById('manage-groups-btn').addEventListener('click', openGroupManager);
document.getElementById('close-group-manager-btn').addEventListener('click', () => {
    document.getElementById('group-management-modal').classList.remove('visible');
    // 刷新聊天设置里的分组列表
    const chatSettingsBtn = document.getElementById('chat-settings-btn');
    if (document.getElementById('chat-settings-modal').classList.contains('visible')) {
       chatSettingsBtn.click(); // 再次点击以重新打开
    }
});

document.getElementById('add-new-group-btn').addEventListener('click', addNewGroup);
document.getElementById('existing-groups-list').addEventListener('click', (e) => {
    if (e.target.classList.contains('delete-group-btn')) {
        const groupId = parseInt(e.target.dataset.id);
        deleteGroup(groupId);
    }
});
// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 请将这段【新代码】粘贴到 init() 的事件监听器区域末尾 ▼▼▼
// 消息操作菜单的按钮事件
document.getElementById('cancel-message-action-btn').addEventListener('click', hideMessageActions);
// ▼▼▼ 【修正】使用新的编辑器入口 ▼▼▼
document.getElementById('edit-message-btn').addEventListener('click', openAdvancedMessageEditor);
// ▲▲▲ 替换结束 ▲▲▲
document.getElementById('copy-message-btn').addEventListener('click', copyMessageContent);

// ▼▼▼ 在这里添加新代码 ▼▼▼
document.getElementById('recall-message-btn').addEventListener('click', handleRecallClick);
// ▲▲▲ 添加结束 ▲▲▲

// ▼▼▼ 请用这段【修正后】的代码替换旧的 select-message-btn 事件监听器 ▼▼▼
document.getElementById('select-message-btn').addEventListener('click', () => {
    // 【核心修复】在关闭菜单前，先捕获时间戳
    const timestampToSelect = activeMessageTimestamp; 
    hideMessageActions();
    // 使用捕获到的值
    if (timestampToSelect) {
        enterSelectionMode(timestampToSelect);
    }
});
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 在 init() 函数的事件监听器区域末尾添加 ▼▼▼

// 动态操作菜单的按钮事件
document.getElementById('edit-post-btn').addEventListener('click', openPostEditor);
document.getElementById('copy-post-btn').addEventListener('click', copyPostContent);
document.getElementById('cancel-post-action-btn').addEventListener('click', hidePostActions);

// ▲▲▲ 添加结束 ▲▲▲

// ▼▼▼ 【新增】联系人选择器事件绑定 ▼▼▼
document.getElementById('cancel-contact-picker-btn').addEventListener('click', () => {
    showScreen('chat-list-screen');
});

document.getElementById('contact-picker-list').addEventListener('click', (e) => {
    const item = e.target.closest('.contact-picker-item');
    if (!item) return;

    const contactId = item.dataset.contactId;
    item.classList.toggle('selected');
    
    if (selectedContacts.has(contactId)) {
        selectedContacts.delete(contactId);
    } else {
        selectedContacts.add(contactId);
    }
    updateContactPickerConfirmButton();
});

// ▼▼▼ 【新增】绑定“管理群成员”按钮事件 ▼▼▼
document.getElementById('manage-members-btn').addEventListener('click', () => {
    // 在切换屏幕前，先隐藏当前的聊天设置弹窗
    document.getElementById('chat-settings-modal').classList.remove('visible');
    // 然后再打开成员管理屏幕
    openMemberManagementScreen();
});
// ▲▲▲ 新增代码结束 ▲▲▲

// ▼▼▼ 【最终完整版】群成员管理功能事件绑定 ▼▼▼
document.getElementById('back-from-member-management').addEventListener('click', () => {

    showScreen('chat-interface-screen');    
    document.getElementById('chat-settings-btn').click();
});
// ▲▲▲ 替换结束 ▲▲▲

document.getElementById('member-management-list').addEventListener('click', (e) => {
    // 【已恢复】移除成员的事件
    if (e.target.classList.contains('remove-member-btn')) {
        removeMemberFromGroup(e.target.dataset.memberId);
    }
});

document.getElementById('add-existing-contact-btn').addEventListener('click', async () => {
    // 【已恢复】从好友列表添加的事件
    // 【关键】为“完成”按钮绑定“拉人入群”的逻辑
    const confirmBtn = document.getElementById('confirm-contact-picker-btn');
    // 使用克隆节点方法清除旧的事件监听器，防止重复绑定
    const newConfirmBtn = confirmBtn.cloneNode(true);
    confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
    newConfirmBtn.addEventListener('click', handleAddMembersToGroup);
    
    await openContactPickerForAddMember();
});

document.getElementById('create-new-member-btn').addEventListener('click', createNewMemberInGroup);
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【全新】视频通话功能事件监听器 ▼▼▼

// 绑定单聊和群聊的发起按钮
document.getElementById('video-call-btn').addEventListener('click', handleInitiateCall);
document.getElementById('group-video-call-btn').addEventListener('click', handleInitiateCall);

// 绑定“挂断”按钮
document.getElementById('hang-up-btn').addEventListener('click', endVideoCall);

// 绑定“取消呼叫”按钮
document.getElementById('cancel-call-btn').addEventListener('click', () => {
    videoCallState.isAwaitingResponse = false;
    showScreen('chat-interface-screen');
});

// 【全新】绑定“加入通话”按钮
document.getElementById('join-call-btn').addEventListener('click', handleUserJoinCall);

// ▼▼▼ 用这个【已修复并激活旁观模式】的版本替换旧的 decline-call-btn 事件监听器 ▼▼▼
// 绑定来电请求的“拒绝”按钮
document.getElementById('decline-call-btn').addEventListener('click', async () => {
    stopRingtone(); 
    hideIncomingCallModal();
    const callerChatId = videoCallState.activeChatId; // 【核心修正1】从专用信道获取来电者ID
    if (!callerChatId) return;

    const chat = state.chats[callerChatId];
    if (!chat) return;
    
    // 【核心修正2】根据是否群聊，执行不同的拒绝逻辑
    if (videoCallState.isGroupCall) {
        // 对于群聊，拒绝=旁观，这个逻辑不变
        videoCallState.isUserParticipating = false;
        const systemNote = {
            role: 'system',
            content: `[系统提示：用户拒绝了通话邀请，但你们可以自己开始。请你们各自决策是否加入。]`,
            timestamp: Date.now(),
            isHidden: true
        };
        chat.history.push(systemNote);
        await db.chats.put(chat);
        await triggerAiResponse(); 
    } else { 
        // 对于单聊，我们不再打扰用户当前界面，而是静默处理
        const declineMessage = { role: 'user', content: '我拒绝了你的视频通话请求。', timestamp: Date.now(), isHidden: true };
        chat.history.push(declineMessage);
        await db.chats.put(chat);
        
        // 【核心修正3】只通知，不切换屏幕
        showNotification(callerChatId, "你已拒绝通话邀请。");
        // 【重要】在后台为对方触发一个响应，让它知道自己被拒绝了
        // 我们需要临时切换activeChatId来触发，然后再换回来
        const originalActiveChatId = state.activeChatId;
        state.activeChatId = callerChatId;
        await triggerAiResponse();
        state.activeChatId = originalActiveChatId;
    }
    
    // 清理状态
    videoCallState.isAwaitingResponse = false;
    videoCallState.activeChatId = null;
});
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 用这个【已修复重复头像BUG】的版本替换旧的 accept-call-btn 事件监听器 ▼▼▼
// 绑定来电请求的“接听”按钮
document.getElementById('accept-call-btn').addEventListener('click', async () => {
    stopRingtone();
    hideIncomingCallModal();
    const callerChatId = videoCallState.activeChatId; // 【核心修正1】从专用信道获取ID
    if (!callerChatId) return;
    
    // 【核心修正2】在接听时，我们才真正改变全局状态，并打开通话界面
    state.activeChatId = callerChatId; // <-- 在这里，我们才授权修改全局状态！
    
    videoCallState.initiator = 'ai';
    videoCallState.isUserParticipating = true;
    
    if (videoCallState.isGroupCall) {
        const chat = state.chats[videoCallState.activeChatId];
        const requester = chat.members.find(m => m.name === videoCallState.callRequester);
        if (requester) {
            videoCallState.participants = [requester];
        } else {
            videoCallState.participants = [];
        }
    }
    
    startVideoCall(); // 启动通话界面
});
// ▲▲▲ 替换结束 ▲▲▲


// ▼▼▼ 请用这个【已增加用户高亮】的全新版本，完整替换旧的 user-speak-btn 事件监听器 ▼▼▼
// 绑定用户在通话中发言的按钮
document.getElementById('user-speak-btn').addEventListener('click', async () => {
    if (!videoCallState.isActive) return;

    // ★★★★★ 核心新增：在弹出输入框前，先找到并高亮用户头像 ★★★★★
    const userAvatar = document.querySelector('.participant-avatar-wrapper[data-participant-id="user"] .participant-avatar');
    if (userAvatar) {
        userAvatar.classList.add('speaking');
    }

    const userInput = await showCustomPrompt('你说', '请输入你想说的话...');
    
    // ★★★★★ 核心新增：无论用户是否输入，只要关闭输入框就移除高亮 ★★★★★
    if (userAvatar) {
        userAvatar.classList.remove('speaking');
    }

    if (userInput && userInput.trim()) {
        triggerAiInCallAction(userInput.trim());
    }
});
// ▲▲▲ 替换结束 ▲▲▲

// ▼▼▼ 【新增】回忆录相关事件绑定 ▼▼▼
// 1. 将“回忆”页签和它的视图连接起来
document.querySelector('.nav-item[data-view="memories-view"]').addEventListener('click', () => {
    // 在切换前，确保"收藏"页面的编辑模式已关闭
    if (isFavoritesSelectionMode) {
        document.getElementById('favorites-edit-btn').click(); 
    }
    switchToChatListView('memories-view');
    renderMemoriesScreen(); // 点击时渲染
});

// 2. 绑定回忆录界面的返回按钮
document.getElementById('memories-back-btn').addEventListener('click', () => switchToChatListView('messages-view'));

// ▲▲▲ 新增结束 ▲▲▲

// 【全新】约定/倒计时功能事件绑定
document.getElementById('add-countdown-btn').addEventListener('click', () => {
    document.getElementById('create-countdown-modal').classList.add('visible');
});
document.getElementById('cancel-create-countdown-btn').addEventListener('click', () => {
    document.getElementById('create-countdown-modal').classList.remove('visible');
});
document.getElementById('confirm-create-countdown-btn').addEventListener('click', async () => {
    const title = document.getElementById('countdown-title-input').value.trim();
    const dateValue = document.getElementById('countdown-date-input').value;
    
    if (!title || !dateValue) {
        alert('请填写完整的约定标题和日期！');
        return;
    }

    const targetDate = new Date(dateValue);
    if (isNaN(targetDate) || targetDate <= new Date()) {
        alert('请输入一个有效的、未来的日期！');
        return;
    }

    const newCountdown = {
        chatId: null, // 用户创建的，不属于任何特定AI
        authorName: '我',
        description: title,
        timestamp: Date.now(),
        type: 'countdown',
        targetDate: targetDate.getTime()
    };
    
    await db.memories.add(newCountdown);
    document.getElementById('create-countdown-modal').classList.remove('visible');
    renderMemoriesScreen();
});

// 【全新】拉黑功能事件绑定
document.getElementById('block-chat-btn').addEventListener('click', async () => {
    if (!state.activeChatId || state.chats[state.activeChatId].isGroup) return;

    const chat = state.chats[state.activeChatId];
    const confirmed = await showCustomConfirm(
        '确认拉黑', 
        `确定要拉黑“${chat.name}”吗？拉黑后您将无法向其发送消息，直到您将Ta移出黑名单，或等待Ta重新申请好友。`,
        { confirmButtonClass: 'btn-danger' }
    );

    if (confirmed) {
        chat.relationship.status = 'blocked_by_user';
        chat.relationship.blockedTimestamp = Date.now();

        // ▼▼▼ 在这里添加下面的代码 ▼▼▼
        const hiddenMessage = {
            role: 'system',
            content: `[系统提示：你刚刚被用户拉黑了。在对方解除拉黑之前，你无法再主动发起对话，也无法回应。]`,
            timestamp: Date.now() + 1,
            isHidden: true
        };
        chat.history.push(hiddenMessage);
        // ▲▲▲ 添加结束 ▲▲▲

        await db.chats.put(chat);
        
        // 关闭设置弹窗，并刷新聊天界面
        document.getElementById('chat-settings-modal').classList.remove('visible');
        renderChatInterface(state.activeChatId);
        // 刷新聊天列表，可能会有UI变化
        renderChatList();
    }
});

document.getElementById('chat-lock-overlay').addEventListener('click', async (e) => {
    const chat = state.chats[state.activeChatId];
    if (!chat) return;

    if (e.target.id === 'force-apply-check-btn') {
        alert("正在手动触发好友申请流程，请稍后...\n如果API调用成功，将弹出提示。如果失败，也会有错误提示。如果长时间无反应，说明AI可能决定暂时不申请。");
        await triggerAiFriendApplication(chat.id);
        renderChatInterface(chat.id); 
        return;
    }

    if (e.target.id === 'unblock-btn') {
        chat.relationship.status = 'friend';
        chat.relationship.blockedTimestamp = null;

        // ▼▼▼ 在这里添加下面的代码 ▼▼▼
        const hiddenMessage = {
            role: 'system',
            content: `[系统提示：用户刚刚解除了对你的拉黑。现在你们可以重新开始对话了。]`,
            timestamp: Date.now(),
            isHidden: true
        };
        chat.history.push(hiddenMessage);
        // ▲▲▲ 添加结束 ▲▲▲

        await db.chats.put(chat);
        renderChatInterface(chat.id);
        renderChatList();
        triggerAiResponse(); // 【可选但推荐】解除后让AI主动说点什么
    }
    else if (e.target.id === 'accept-friend-btn') {
        chat.relationship.status = 'friend';
        chat.relationship.applicationReason = '';

        // ▼▼▼ 在这里添加下面的代码 ▼▼▼
        const hiddenMessage = {
            role: 'system',
            content: `[系统提示：用户刚刚通过了你的好友申请。你们现在又可以正常聊天了。]`,
            timestamp: Date.now(),
            isHidden: true
        };
        chat.history.push(hiddenMessage);
        // ▲▲▲ 添加结束 ▲▲▲

        await db.chats.put(chat);
        renderChatInterface(chat.id);
        renderChatList();
        const msg = { role: 'user', content: '我通过了你的好友请求', timestamp: Date.now() };
        chat.history.push(msg);
        await db.chats.put(chat);
        appendMessage(msg, chat);
        triggerAiResponse();
    }
    else if (e.target.id === 'reject-friend-btn') {
        chat.relationship.status = 'blocked_by_user';
        chat.relationship.blockedTimestamp = Date.now();
        chat.relationship.applicationReason = '';
        await db.chats.put(chat);
        renderChatInterface(chat.id);
    }
    // 【新增】处理申请好友按钮的点击事件
    else if (e.target.id === 'apply-friend-btn') {
        const reason = await showCustomPrompt(
            '发送好友申请', 
            `请输入你想对“${chat.name}”说的申请理由：`,
            "我们和好吧！"
        );
        // 只有当用户输入了内容并点击“确定”后才继续
        if (reason !== null) {
            // 更新关系状态为“等待AI批准”
            chat.relationship.status = 'pending_ai_approval';
            chat.relationship.applicationReason = reason;
            await db.chats.put(chat);

            // 刷新UI，显示“等待通过”的界面
            renderChatInterface(chat.id);
            renderChatList();
            
            // 【关键】触发AI响应，让它去处理这个好友申请
            triggerAiResponse();
        }
    }
});

// ▼▼▼ 【全新】红包功能事件绑定 ▼▼▼

// 1. 将原有的转账按钮(￥)的点击事件，重定向到新的总入口函数
document.getElementById('transfer-btn').addEventListener('click', handlePaymentButtonClick);

// 2. 红包模态框内部的控制按钮
document.getElementById('cancel-red-packet-btn').addEventListener('click', () => {
    document.getElementById('red-packet-modal').classList.remove('visible');
});
document.getElementById('send-group-packet-btn').addEventListener('click', sendGroupRedPacket);
document.getElementById('send-direct-packet-btn').addEventListener('click', sendDirectRedPacket);

// 3. 红包模态框的页签切换逻辑
const rpTabGroup = document.getElementById('rp-tab-group');
const rpTabDirect = document.getElementById('rp-tab-direct');
const rpContentGroup = document.getElementById('rp-content-group');
const rpContentDirect = document.getElementById('rp-content-direct');

rpTabGroup.addEventListener('click', () => {
    rpTabGroup.classList.add('active');
    rpTabDirect.classList.remove('active');
    rpContentGroup.style.display = 'block';
    rpContentDirect.style.display = 'none';
});
rpTabDirect.addEventListener('click', () => {
    rpTabDirect.classList.add('active');
    rpTabGroup.classList.remove('active');
    rpContentDirect.style.display = 'block';
    rpContentGroup.style.display = 'none';
});

// 4. 实时更新红包金额显示
document.getElementById('rp-group-amount').addEventListener('input', (e) => {
    const amount = parseFloat(e.target.value) || 0;
    document.getElementById('rp-group-total').textContent = `¥ ${amount.toFixed(2)}`;
});
document.getElementById('rp-direct-amount').addEventListener('input', (e) => {
    const amount = parseFloat(e.target.value) || 0;
    document.getElementById('rp-direct-total').textContent = `¥ ${amount.toFixed(2)}`;
});

// ▲▲▲ 新事件绑定结束 ▲▲▲

// ▼▼▼ 【全新添加】使用事件委托处理红包点击，修复失效问题 ▼▼▼
document.getElementById('chat-messages').addEventListener('click', (e) => {
    // 1. 找到被点击的红包卡片
    const packetCard = e.target.closest('.red-packet-card');
    if (!packetCard) return; // 如果点击的不是红包，就什么也不做

    // 2. 从红包卡片的父级.message-bubble获取时间戳
    const messageBubble = packetCard.closest('.message-bubble');
    if (!messageBubble || !messageBubble.dataset.timestamp) return;

    // 3. 调用我们现有的处理函数
    const timestamp = parseInt(messageBubble.dataset.timestamp);
    handlePacketClick(timestamp);
});
// ▲▲▲ 新增代码结束 ▲▲▲

// ▼▼▼ 【全新】投票功能事件监听器 ▼▼▼
// 在输入框工具栏添加按钮
document.getElementById('send-poll-btn').addEventListener('click', openCreatePollModal);

// 投票创建模态框的按钮
document.getElementById('add-poll-option-btn').addEventListener('click', addPollOptionInput);
document.getElementById('cancel-create-poll-btn').addEventListener('click', () => {
    document.getElementById('create-poll-modal').classList.remove('visible');
});
document.getElementById('confirm-create-poll-btn').addEventListener('click', sendPoll);

// 使用事件委托处理投票卡片内的所有点击事件
document.getElementById('chat-messages').addEventListener('click', (e) => {
    const pollCard = e.target.closest('.poll-card');
    if (!pollCard) return;

    const timestamp = parseInt(pollCard.dataset.pollTimestamp);
    if (isNaN(timestamp)) return;
    
    // 点击了选项
    const optionItem = e.target.closest('.poll-option-item');
    if (optionItem && !pollCard.classList.contains('closed')) {
        handleUserVote(timestamp, optionItem.dataset.option);
        return;
    }
    
    // 点击了动作按钮（结束投票/查看结果）
    const actionBtn = e.target.closest('.poll-action-btn');
    if (actionBtn) {
        if (pollCard.classList.contains('closed')) {
            showPollResults(timestamp);
        } else {
            endPoll(timestamp);
        }
        return;
    }

    // 如果是已结束的投票，点击卡片任何地方都可以查看结果
    if (pollCard.classList.contains('closed')) {
        showPollResults(timestamp);
    }
});
// ▲▲▲ 新事件监听器粘贴结束 ▲▲▲

  // ▼▼▼ 【全新】AI头像库功能事件绑定 ▼▼▼
document.getElementById('manage-ai-avatar-library-btn').addEventListener('click', openAiAvatarLibraryModal);
document.getElementById('add-ai-avatar-btn').addEventListener('click', addAvatarToLibrary);
document.getElementById('close-ai-avatar-library-btn').addEventListener('click', closeAiAvatarLibraryModal);
// ▲▲▲ 新增结束 ▲▲▲

// ▼▼▼ 在 init() 的事件监听区域，粘贴这段【新代码】▼▼▼
document.getElementById('icon-settings-grid').addEventListener('click', async (e) => {
    if (e.target.classList.contains('change-icon-btn')) {
        const item = e.target.closest('.icon-setting-item');
        const iconId = item.dataset.iconId;
        if (!iconId) return;

        const currentUrl = state.globalSettings.appIcons[iconId];
        const newUrl = await showCustomPrompt(`更换“${item.querySelector('.icon-preview').alt}”图标`, '请输入新的图片URL', currentUrl, 'url');

        if (newUrl && newUrl.trim().startsWith('http')) {
            // 仅在内存中更新，等待用户点击“保存”
            state.globalSettings.appIcons[iconId] = newUrl.trim();
            // 实时更新设置页面的预览图
            item.querySelector('.icon-preview').src = newUrl.trim();
        } else if (newUrl !== null) {
            alert("请输入一个有效的URL！");
        }
    }
});
// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 在 init() 函数的末尾，粘贴这段【全新的事件监听器】 ▼▼▼

    document.getElementById('chat-messages').addEventListener('click', (e) => {
        // 使用 .closest() 向上查找被点击的卡片
        const linkCard = e.target.closest('.link-share-card');
        if (linkCard) {
            const timestamp = parseInt(linkCard.dataset.timestamp);
            if (!isNaN(timestamp)) {
                openBrowser(timestamp); // 调用我们的函数
            }
        }
    });

    // 浏览器返回按钮的事件监听，确保它只绑定一次
    document.getElementById('browser-back-btn').addEventListener('click', () => {
        showScreen('chat-interface-screen');
    });

// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 在 init() 函数的末尾，粘贴这段【全新的事件监听器】 ▼▼▼

    // 1. 绑定输入框上方“分享链接”按钮的点击事件
    document.getElementById('share-link-btn').addEventListener('click', openShareLinkModal);

    // 2. 绑定模态框中“取消”按钮的点击事件
    document.getElementById('cancel-share-link-btn').addEventListener('click', () => {
        document.getElementById('share-link-modal').classList.remove('visible');
    });

    // 3. 绑定模态框中“分享”按钮的点击事件
    document.getElementById('confirm-share-link-btn').addEventListener('click', sendUserLinkShare);

// ▲▲▲ 新代码粘贴结束 ▲▲▲

document.getElementById('theme-toggle-switch').addEventListener('change', toggleTheme);

// ▼▼▼ 在 init() 的事件监听器区域，粘贴下面这几行 ▼▼▼
// 绑定消息操作菜单中的“引用”按钮
document.getElementById('quote-message-btn').addEventListener('click', startReplyToMessage);

// 绑定回复预览栏中的“取消”按钮
document.getElementById('cancel-reply-btn').addEventListener('click', cancelReplyMode);
// ▲▲▲ 粘贴结束 ▲▲▲

// 在你的 init() 函数的事件监听器区域...

// ▼▼▼ 用这段代码替换旧的转账卡片点击事件 ▼▼▼
document.getElementById('chat-messages').addEventListener('click', (e) => {
    // 1. 向上查找被点击的元素是否在一个消息气泡内
    const bubble = e.target.closest('.message-bubble');
    if (!bubble) return; // 如果不在，就退出

    // 2. 【核心修正】在这里添加严格的筛选条件
    // 必须是 AI 的消息 (.ai)
    // 必须是转账类型 (.is-transfer)
    // 必须是我们标记为“待处理”的 (data-status="pending")
    if (bubble.classList.contains('ai') && 
        bubble.classList.contains('is-transfer') && 
        bubble.dataset.status === 'pending') {
        
        // 3. 只有满足所有条件，才执行后续逻辑
        const timestamp = parseInt(bubble.dataset.timestamp);
        if (!isNaN(timestamp)) {
            showTransferActionModal(timestamp);
        }
    }
});
// ▲▲▲ 替换结束 ▲▲▲

// 在 init() 的事件监听区域添加
document.getElementById('transfer-action-accept').addEventListener('click', () => handleUserTransferResponse('accepted'));
document.getElementById('transfer-action-decline').addEventListener('click', () => handleUserTransferResponse('declined'));
document.getElementById('transfer-action-cancel').addEventListener('click', hideTransferActionModal);

// ▼▼▼ 用这段【新代码】替换旧的通话记录事件绑定 ▼▼▼

document.getElementById('chat-list-title').addEventListener('click', renderCallHistoryScreen);

// 2. 绑定通话记录页面的“返回”按钮
document.getElementById('call-history-back-btn').addEventListener('click', () => {
    // 【核心修改】返回到聊天列表页面，而不是聊天界面
    showScreen('chat-list-screen');
});

// 3. 监听卡片点击的逻辑保持不变
document.getElementById('call-history-list').addEventListener('click', (e) => {
    const card = e.target.closest('.call-record-card');
    if (card && card.dataset.recordId) {
        showCallTranscript(parseInt(card.dataset.recordId));
    }
});

// 4. 关闭详情弹窗的逻辑保持不变
document.getElementById('close-transcript-modal-btn').addEventListener('click', () => {
    document.getElementById('call-transcript-modal').classList.remove('visible');
});

// ▲▲▲ 替换结束 ▲▲▲

document.getElementById('chat-messages').addEventListener('click', (e) => {
    // 1. 检查点击的是否是语音条
    const voiceBody = e.target.closest('.voice-message-body');
    if (!voiceBody) return;

    // 2. 找到相关的DOM元素
    const bubble = voiceBody.closest('.message-bubble');
    if (!bubble) return;
    
    const spinner = voiceBody.querySelector('.loading-spinner');
    const transcriptEl = bubble.querySelector('.voice-transcript');

    // 如果正在加载中，则不响应点击
    if (bubble.dataset.state === 'loading') {
        return;
    }

    // 3. 如果文字已经展开，则收起
    if (bubble.dataset.state === 'expanded') {
        transcriptEl.style.display = 'none';
        bubble.dataset.state = 'collapsed';
    } 
    // 4. 如果是收起状态，则开始“转录”流程
    else {
        bubble.dataset.state = 'loading'; // 进入加载状态
        spinner.style.display = 'block';   // 显示加载动画

        // 模拟1.5秒的语音识别过程
        setTimeout(() => {
            // 检查此时元素是否还存在（可能用户已经切换了聊天）
            if (document.body.contains(bubble)) {
                const voiceText = bubble.dataset.voiceText || '(无法识别)';
                transcriptEl.textContent = voiceText; // 填充文字
                
                spinner.style.display = 'none';      // 隐藏加载动画
                transcriptEl.style.display = 'block';// 显示文字
                bubble.dataset.state = 'expanded';     // 进入展开状态
            }
        }, 500);
    }
});

document.getElementById('chat-header-status').addEventListener('click', handleEditStatusClick);

// 在 init() 的事件监听器区域添加
document.getElementById('selection-share-btn').addEventListener('click', () => {
    if (selectedMessages.size > 0) {
        openShareTargetPicker(); // 打开我们即将创建的目标选择器
    }
});

// 在 init() 的事件监听器区域添加
document.getElementById('confirm-share-target-btn').addEventListener('click', async () => {
    const sourceChat = state.chats[state.activeChatId];
    const selectedTargetIds = Array.from(document.querySelectorAll('.share-target-checkbox:checked'))
                                   .map(cb => cb.dataset.chatId);

    if (selectedTargetIds.length === 0) {
        alert("请至少选择一个要分享的聊天。");
        return;
    }

    // 1. 打包聊天记录
    const sharedHistory = [];
    const sortedTimestamps = [...selectedMessages].sort((a, b) => a - b);
    for (const timestamp of sortedTimestamps) {
        const msg = sourceChat.history.find(m => m.timestamp === timestamp);
        if (msg) {
            sharedHistory.push(msg);
        }
    }
    
    // 2. 创建分享卡片消息对象
    const shareCardMessage = {
        role: 'user',
        senderName: sourceChat.isGroup ? (sourceChat.settings.myNickname || '我') : '我',
        type: 'share_card',
        timestamp: Date.now(),
        payload: {
            sourceChatName: sourceChat.name,
            title: `来自“${sourceChat.name}”的聊天记录`,
            sharedHistory: sharedHistory
        }
    };

    // 3. 循环发送到所有目标聊天
    for (const targetId of selectedTargetIds) {
        const targetChat = state.chats[targetId];
        if (targetChat) {
            targetChat.history.push(shareCardMessage);
            await db.chats.put(targetChat);
        }
    }
    
    // 4. 收尾工作
    document.getElementById('share-target-modal').classList.remove('visible');
    exitSelectionMode(); // 退出多选模式
    await showCustomAlert("分享成功", `聊天记录已成功分享到 ${selectedTargetIds.length} 个会话中。`);
    renderChatList(); // 刷新列表，可能会有新消息提示
});

// 绑定取消按钮
document.getElementById('cancel-share-target-btn').addEventListener('click', () => {
    document.getElementById('share-target-modal').classList.remove('visible');
});

// 在 init() 的事件监听器区域添加
document.getElementById('chat-messages').addEventListener('click', (e) => {
    // ...你已有的其他点击事件逻辑...

    // 新增逻辑：处理分享卡片的点击
    const shareCard = e.target.closest('.link-share-card[data-timestamp]');
    if (shareCard && shareCard.closest('.message-bubble.is-link-share')) {
        const timestamp = parseInt(shareCard.dataset.timestamp);
        openSharedHistoryViewer(timestamp);
    }
});

// 绑定查看器的关闭按钮
document.getElementById('close-shared-history-viewer-btn').addEventListener('click', () => {
    document.getElementById('shared-history-viewer-modal').classList.remove('visible');
});

// 创建新函数来处理渲染逻辑
function openSharedHistoryViewer(timestamp) {
    const chat = state.chats[state.activeChatId];
    const message = chat.history.find(m => m.timestamp === timestamp);
    if (!message || message.type !== 'share_card') return;

    const viewerModal = document.getElementById('shared-history-viewer-modal');
    const viewerTitle = document.getElementById('shared-history-viewer-title');
    const viewerContent = document.getElementById('shared-history-viewer-content');

    viewerTitle.textContent = message.payload.title;
    viewerContent.innerHTML = ''; // 清空旧内容

    // 【核心】复用 createMessageElement 来渲染每一条被分享的消息
    message.payload.sharedHistory.forEach(sharedMsg => {
        // 注意：这里我们传入的是 sourceChat 对象，以确保头像、昵称等正确
        const sourceChat = Object.values(state.chats).find(c => c.name === message.payload.sourceChatName) || chat;
        const bubbleEl = createMessageElement(sharedMsg, sourceChat);
        if (bubbleEl) {
            viewerContent.appendChild(bubbleEl);
        }
    });

    viewerModal.classList.add('visible');
}

audioPlayer.addEventListener('timeupdate', updateMusicProgressBar);

audioPlayer.addEventListener('pause', () => { 
    if(musicState.isActive) { 
        musicState.isPlaying = false; 
        updatePlayerUI(); 
    } 
});
audioPlayer.addEventListener('play', () => { 
    if(musicState.isActive) { 
        musicState.isPlaying = true; 
        updatePlayerUI(); 
    } 
});

document.getElementById('playlist-body').addEventListener('click', async (e) => {
    const target = e.target;
    if (target.classList.contains('delete-track-btn')) {
        const index = parseInt(target.dataset.index);
        const track = musicState.playlist[index];
        const confirmed = await showCustomConfirm('删除歌曲', `确定要从播放列表中删除《${track.name}》吗？`);
        if (confirmed) {
            deleteTrack(index);
        }
        return;
    }
    if (target.classList.contains('lyrics-btn')) {
        const index = parseInt(target.dataset.index);
        if (isNaN(index)) return;
        const lrcContent = await new Promise(resolve => {
            const lrcInput = document.getElementById('lrc-upload-input');
            const handler = (event) => {
                const file = event.target.files[0];
                if (file) {
                    const reader = new FileReader();
                    reader.onload = (re) => resolve(re.target.result);
                    reader.readAsText(file);
                } else {
                    resolve(null);
                }
                lrcInput.removeEventListener('change', handler);
                lrcInput.value = '';
            };
            lrcInput.addEventListener('change', handler);
            lrcInput.click();
        });
        if (lrcContent !== null) {
            musicState.playlist[index].lrcContent = lrcContent;
            await saveGlobalPlaylist();
            alert('歌词导入成功！');
            if (musicState.currentIndex === index) {
                musicState.parsedLyrics = parseLRC(lrcContent);
                renderLyrics();
            }
        }
    }
});

document.querySelector('.progress-bar').addEventListener('click', (e) => {
    if (!audioPlayer.duration) return;
    const progressBar = e.currentTarget;
    const barWidth = progressBar.clientWidth;
    const clickX = e.offsetX;
    audioPlayer.currentTime = (clickX / barWidth) * audioPlayer.duration;
});

// ▼▼▼ 在 init() 函数的事件监听器区域，粘贴这段新代码 ▼▼▼

// 使用事件委托来处理所有“已撤回消息”的点击事件
document.getElementById('chat-messages').addEventListener('click', (e) => {
    // 检查被点击的元素或其父元素是否是“已撤回”提示
    const placeholder = e.target.closest('.recalled-message-placeholder');
    if (!placeholder) return; // 如果不是，就退出

    // 如果是，就从聊天记录中找到对应的数据并显示
    const chat = state.chats[state.activeChatId];
    const wrapper = placeholder.closest('.message-wrapper'); // 找到它的父容器
    if (chat && wrapper) {
        // 从父容器上找到时间戳
        const timestamp = parseInt(wrapper.dataset.timestamp);
        const recalledMsg = chat.history.find(m => m.timestamp === timestamp);
        
        if (recalledMsg && recalledMsg.recalledData) {
            let originalContentText = '';
            const recalled = recalledMsg.recalledData;
            
            if (recalled.originalType === 'text') {
                originalContentText = `原文: "${recalled.originalContent}"`;
            } else {
                originalContentText = `撤回了一条[${recalled.originalType}]类型的消息`;
            }
            showCustomAlert('已撤回的消息', originalContentText);
        }
    }
});

// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 在 init() 的事件监听器区域，粘贴这段新代码 ▼▼▼
document.getElementById('manage-world-book-categories-btn').addEventListener('click', openCategoryManager);
document.getElementById('close-category-manager-btn').addEventListener('click', () => {
    document.getElementById('world-book-category-manager-modal').classList.remove('visible');
    renderWorldBookScreen(); // 关闭后刷新主列表
});
document.getElementById('add-new-category-btn').addEventListener('click', addNewCategory);
document.getElementById('existing-categories-list').addEventListener('click', (e) => {
    if (e.target.classList.contains('delete-group-btn')) {
        const categoryId = parseInt(e.target.dataset.id);
        deleteCategory(categoryId);
    }
});
// ▲▲▲ 新代码粘贴结束 ▲▲▲

// --- ▼▼▼ 【全新】自定义头像框功能事件绑定 ▼▼▼ ---

// 打开“选择”弹窗的按钮
document.getElementById('chat-settings-modal').addEventListener('click', (e) => {
    if (e.target.classList.contains('change-frame-btn')) {
        openFrameSelectorModal(e.target.dataset.type);
    }
});
document.getElementById('member-settings-modal').addEventListener('click', (e) => {
    if (e.target.classList.contains('change-frame-btn')) {
        openFrameSelectorModal('member', editingMemberId);
    }
});

// “选择”弹窗内的按钮
// “选择”弹窗内的按钮（已修正）
document.getElementById('manage-custom-frames-btn').addEventListener('click', () => {
    // 1. 先关闭当前的选择弹窗
    document.getElementById('avatar-frame-modal').classList.remove('visible');
    
    // 2. 然后再打开管理弹窗
    openFrameManager();
});
document.getElementById('cancel-frame-settings-btn').addEventListener('click', () => document.getElementById('avatar-frame-modal').classList.remove('visible'));
document.getElementById('save-frame-settings-btn').addEventListener('click', saveSelectedFrames);

// “管理”弹窗内的按钮
document.getElementById('upload-custom-frame-btn').addEventListener('click', handleUploadCustomFrame);
document.getElementById('close-frame-manager-btn').addEventListener('click', () => {
    document.getElementById('custom-frame-manager-modal').classList.remove('visible');
    // 关闭管理后，刷新选择界面，因为列表可能变了
    openFrameSelectorModal(currentFrameSelection.type, currentFrameSelection.target);
});

// ▲▲▲ 新事件绑定结束 ▲▲▲

// ▼▼▼ 【全新】聊天列表左滑功能JS逻辑 ▼▼▼
const chatListEl = document.getElementById('chat-list');
let chatSwipeState = { isDragging: false, startX: 0, activeContent: null };

// 关闭所有已滑开的项
function resetAllChatSwipes(exceptThisOne = null) {
    document.querySelectorAll('.chat-list-item-content.swiped').forEach(content => {
        if (content !== exceptThisOne) {
            content.classList.remove('swiped');
        }
    });
}

chatListEl.addEventListener('mousedown', (e) => {
    const content = e.target.closest('.chat-list-item-content');
    if (content) {
        resetAllChatSwipes(content);
        chatSwipeState.isDragging = true;
        chatSwipeState.startX = e.pageX;
        chatSwipeState.activeContent = content;
        // 阻止拖动时选中文本
        e.preventDefault();
    }
});

document.addEventListener('mousemove', (e) => {
    if (!chatSwipeState.isDragging || !chatSwipeState.activeContent) return;
    const diffX = e.pageX - chatSwipeState.startX;
    if (diffX < 0 && diffX > -170) { // 只允许向左滑, 限制最大距离
        chatSwipeState.activeContent.style.transition = 'none'; // 滑动时禁用动画
        chatSwipeState.activeContent.style.transform = `translateX(${diffX}px)`;
    }
});

document.addEventListener('mouseup', (e) => {
    if (!chatSwipeState.isDragging || !chatSwipeState.activeContent) return;
    
    chatSwipeState.activeContent.style.transition = 'transform 0.3s ease';
    const transformStyle = window.getComputedStyle(chatSwipeState.activeContent).transform;
    const currentTranslateX = new DOMMatrix(transformStyle).m41;

    if (currentTranslateX < -60) { // 滑动超过阈值
        chatSwipeState.activeContent.classList.add('swiped');
    } else {
        chatSwipeState.activeContent.classList.remove('swiped');
    }
    chatSwipeState.activeContent.style.transform = ''; // 清除内联样式，交由CSS class控制

    // 重置状态
    chatSwipeState.isDragging = false;
    chatSwipeState.activeContent = null;
});

// 移动端触摸事件的兼容
chatListEl.addEventListener('touchstart', (e) => {
     const content = e.target.closest('.chat-list-item-content');
    if (content) {
        resetAllChatSwipes(content);
        chatSwipeState.isDragging = true;
        chatSwipeState.startX = e.touches[0].pageX;
        chatSwipeState.activeContent = content;
    }
}, { passive: true });

chatListEl.addEventListener('touchmove', (e) => {
    if (!chatSwipeState.isDragging || !chatSwipeState.activeContent) return;
    const diffX = e.touches[0].pageX - chatSwipeState.startX;
     if (diffX < 0 && diffX > -170) {
        chatSwipeState.activeContent.style.transition = 'none';
        chatSwipeState.activeContent.style.transform = `translateX(${diffX}px)`;
    }
}, { passive: true });

chatListEl.addEventListener('touchend', (e) => {
    if (!chatSwipeState.isDragging || !chatSwipeState.activeContent) return;
    
    chatSwipeState.activeContent.style.transition = 'transform 0.3s ease';
    const transformStyle = window.getComputedStyle(chatSwipeState.activeContent).transform;
    const currentTranslateX = new DOMMatrix(transformStyle).m41;

    if (currentTranslateX < -60) {
        chatSwipeState.activeContent.classList.add('swiped');
    } else {
        chatSwipeState.activeContent.classList.remove('swiped');
    }
    chatSwipeState.activeContent.style.transform = '';

    chatSwipeState.isDragging = false;
    chatSwipeState.activeContent = null;
});
// ▲▲▲ 新JS逻辑粘贴结束 ▲▲▲

// ▼▼▼ 【全新】聊天列表操作按钮点击事件 ▼▼▼
chatListEl.addEventListener('click', async (e) => {
    const target = e.target;
    if (target.classList.contains('swipe-action-btn')) {
        const container = target.closest('.chat-list-item-swipe-container');
        if (!container) return;

        const chatId = container.dataset.chatId;
        const chat = state.chats[chatId];
        if (!chat) return;

        if (target.classList.contains('pin') || target.classList.contains('unpin')) {
            // 置顶或取消置顶
            chat.isPinned = !chat.isPinned;
            await db.chats.put(chat);
            await renderChatList(); // 重新渲染列表以更新排序
        } else if (target.classList.contains('delete')) {
            // 删除
            const confirmed = await showCustomConfirm('删除对话', `确定要删除与 "${chat.name}" 的整个对话吗？此操作不可撤销。`, { confirmButtonClass: 'btn-danger' });
            if (confirmed) {
                if (musicState.isActive && musicState.activeChatId === chat.id) await endListenTogetherSession(false);
                delete state.chats[chat.id];
                if (state.activeChatId === chat.id) state.activeChatId = null;
                await db.chats.delete(chat.id);
                await renderChatList();
            } else {
                // 如果取消删除，则把滑块收回去
                const content = container.querySelector('.chat-list-item-content');
                if (content) content.classList.remove('swiped');
            }
        }
    }
});
// ▲▲▲ 新事件监听粘贴结束 ▲▲▲

// ▼▼▼ 在 init() 的事件监听器区域末尾添加 ▼▼▼

// 绑定“查找聊天记录”按钮
document.getElementById('chat-settings-modal').addEventListener('click', (e) => {
    if (e.target.id === 'find-history-btn') {
        openChatSearchScreen();
    }
});

// 绑定搜索页面的返回按钮
document.getElementById('search-back-btn').addEventListener('click', () => {
    // 返回到聊天设置弹窗
    showScreen('chat-interface-screen'); 
    document.getElementById('chat-settings-modal').classList.add('visible');
});

// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 在 init() 的事件监听器区域末尾添加 ▼▼▼

// 为所有搜索输入框绑定事件，任何一个变化都会触发搜索
document.getElementById('keyword-search-input').addEventListener('input', performChatSearch);
document.getElementById('sender-search-select').addEventListener('change', performChatSearch);
document.getElementById('date-search-input').addEventListener('change', performChatSearch);

// 为搜索结果列表绑定点击事件（事件委托）
document.getElementById('search-results-list').addEventListener('click', (e) => {
    const item = e.target.closest('.search-result-item');
    if (item && item.dataset.timestamp) {
        const timestamp = parseInt(item.dataset.timestamp);
        jumpToMessage(timestamp);
    }
});

// ▲▲▲ 新代码粘贴结束 ▲▲▲

    // ▼▼▼ 在 init() 的事件监听器区域末尾，粘贴下面这一整块新代码 ▼▼▼

    // --- 美化功能事件绑定 ---
    const themeEditor = document.getElementById('theme-css-editor');
    
    // 页面加载时，加载主题列表并显示模板
    await loadThemesToDropdown();
    themeEditor.value = THEME_CSS_TEMPLATE;

    // 绑定下拉框选择事件
    document.getElementById('theme-selector').addEventListener('change', handleThemeSelection);
    
    // 绑定所有操作按钮
    document.getElementById('apply-theme-btn').addEventListener('click', () => applyThemeCss(themeEditor.value));
    document.getElementById('save-theme-btn').addEventListener('click', saveCurrentTheme);
    document.getElementById('save-as-new-theme-btn').addEventListener('click', saveAsNewTheme);
    document.getElementById('rename-theme-btn').addEventListener('click', renameSelectedTheme);
    document.getElementById('delete-theme-btn').addEventListener('click', deleteSelectedTheme);
    document.getElementById('export-theme-btn').addEventListener('click', exportTheme);
    
    // 绑定导入按钮和隐藏的文件选择器
    document.getElementById('import-theme-btn').addEventListener('click', () => {
        document.getElementById('import-theme-input').click();
    });
    document.getElementById('import-theme-input').addEventListener('change', (e) => {
        importTheme(e.target.files[0]);
        e.target.value = null; // 清空，以便下次能选择同一个文件
    });
    
    // ▲▲▲ 新事件绑定结束 ▲▲▲

document.getElementById('api-preset-select').addEventListener('change', handleApiPresetSelectChange);
document.getElementById('manage-api-presets-btn').addEventListener('click', openApiPresetManager);

            // ▼▼▼ 【全新】锁屏功能事件监听器 ▼▼▼

            // 1. 锁屏壁纸上传
            document.getElementById('lockscreen-wallpaper-upload-input').addEventListener('change', async (event) => {
                const file = event.target.files[0];
                if(file) {
                    const dataUrl = await new Promise((res, rej) => {
                        const reader = new FileReader();
                        reader.onload = () => res(reader.result);
                        reader.onerror = () => rej(reader.error);
                        reader.readAsDataURL(file);
                    });
                    newLockscreenWallpaperBase64 = dataUrl;
                    renderWallpaperScreen(); // 上传后实时预览
                }
            });

            // 2. 密码输入框按钮
            document.getElementById('password-confirm-btn').addEventListener('click', checkPassword);
            document.getElementById('password-cancel-btn').addEventListener('click', hidePasswordModal);
            // 允许在输入框内按回车键确认
            document.getElementById('password-input-field').addEventListener('keypress', (e) => {
                if (e.key === 'Enter') {
                    checkPassword();
                }
            });

            // 3. 【全新】带有动画效果的上滑解锁手势
            const lockScreen = document.getElementById('lock-screen');
            const unlockHint = document.getElementById('unlock-hint');
            let touchStartY = 0;
            let isSwiping = false;

// 统一的开始处理函数
const handleUnlockStart = (e) => {
    if (document.getElementById('password-modal-overlay').classList.contains('visible')) return;
    
    // (你原来的其他逻辑保持不变)
    const blurBg = document.getElementById('lock-screen-background-blur');
    if (state.globalSettings.password) {
        blurBg.style.backgroundImage = lockScreen.style.backgroundImage;
        blurBg.style.display = 'block';
    } else {
        document.getElementById('home-screen').classList.add('active');
    }
    
    touchStartY = getEventCoords(e).y; // 使用辅助函数获取Y坐标
    isSwiping = true;
    lockScreen.style.transition = 'none';
    unlockHint.style.transition = 'none';
};

// 统一的移动处理函数
const handleUnlockMove = (e) => {
    if (!isSwiping) return;
    const currentY = getEventCoords(e).y; // 使用辅助函数
    let diffY = currentY - touchStartY;
    // (你原来的其他逻辑保持不变)
    if (diffY > 0) diffY = 0;
    lockScreen.style.transform = `translateY(${diffY}px)`;
    unlockHint.style.opacity = Math.max(0, 1 - Math.abs(diffY) / 100);
    if (state.globalSettings.password) {
        const blurBg = document.getElementById('lock-screen-background-blur');
        blurBg.style.opacity = Math.min(1, Math.abs(diffY) / 80);
    }
};

// 统一的结束处理函数
const handleUnlockEnd = (e) => {
    if (!isSwiping) return;
    isSwiping = false;

    // (你原来的其他逻辑保持不变)
    lockScreen.style.transition = 'transform 0.3s ease-out';
    unlockHint.style.transition = 'opacity 0.3s ease-out';
    const blurBg = document.getElementById('lock-screen-background-blur');
    
    // 注意：touchend事件的坐标在 e.changedTouches[0]
    const touchEndY = e.changedTouches ? e.changedTouches[0].clientY : e.pageY;
    const swipeDistance = touchStartY - touchEndY;
    
    if (swipeDistance > 80) {
        lockScreen.style.transform = 'translateY(-100%)';
        setTimeout(() => {
            if (state.globalSettings.password) {
                showPasswordModal();
            } else {
                unlockPhone();
            }
        }, 300);
    } else {
        lockScreen.style.transform = 'translateY(0)';
        unlockHint.style.opacity = '1';
        if (state.globalSettings.password) {
            blurBg.style.opacity = '0';
            setTimeout(() => { blurBg.style.display = 'none'; }, 300);
        } else {
            document.getElementById('home-screen').classList.remove('active');
        }
    }
};

// 2. 绑定两种事件到同一套处理逻辑上
lockScreen.addEventListener('touchstart', handleUnlockStart, { passive: true });
lockScreen.addEventListener('touchmove', handleUnlockMove, { passive: true });
lockScreen.addEventListener('touchend', handleUnlockEnd, { passive: true });

lockScreen.addEventListener('mousedown', handleUnlockStart);
// 注意：mousemove和mouseup最好绑定在document上，防止鼠标拖出范围后失效
document.addEventListener('mousemove', handleUnlockMove);
document.addEventListener('mouseup', handleUnlockEnd);
            
            // ▲▲▲ 新事件监听器粘贴结束 ▲▲▲

// 【全新】为聊天底部工具栏添加鼠标拖动滚动功能
const actionsTopBar = document.getElementById('chat-input-actions-top');
let isDown = false;
let startX;
let scrollLeft;

actionsTopBar.addEventListener('mousedown', (e) => {
    isDown = true;
    actionsTopBar.classList.add('grabbing'); // 添加一个class来改变鼠标样式
    startX = e.pageX - actionsTopBar.offsetLeft;
    scrollLeft = actionsTopBar.scrollLeft;
});

actionsTopBar.addEventListener('mouseleave', () => {
    isDown = false;
    actionsTopBar.classList.remove('grabbing');
});

actionsTopBar.addEventListener('mouseup', () => {
    isDown = false;
    actionsTopBar.classList.remove('grabbing');
});

actionsTopBar.addEventListener('mousemove', (e) => {
    if (!isDown) return;
    e.preventDefault();
    const x = e.pageX - actionsTopBar.offsetLeft;
    const walk = (x - startX) * 2; // 乘以2可以增加拖动速度，感觉更灵敏
    actionsTopBar.scrollLeft = scrollLeft - walk;
});

// ▼▼▼ 在 init() 的事件监听器区域末尾，粘贴这段新代码 ▼▼▼
document.getElementById('location-cancel-btn').addEventListener('click', () => {
    document.getElementById('send-location-modal').classList.remove('visible');
});
document.getElementById('location-confirm-btn').addEventListener('click', sendUserLocation);
// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 在 init() 的事件监听器区域末尾，粘贴这段新代码 ▼▼▼

// 【全新】为定位模态框的“添加途经点”按钮绑定事件
document.getElementById('add-trajectory-point-btn').addEventListener('click', () => {
    // 限制最多添加3个途经点，防止UI过于拥挤
    if (document.querySelectorAll('.trajectory-point-input').length < 3) {
        addTrajectoryPointInput();
    } else {
        alert("最多只能添加3个途经点哦！");
    }
});

// 【新增】打开定位模态框时，清空旧的途经点
document.getElementById('send-location-btn').addEventListener('click', () => {
    document.getElementById('trajectory-points-container').innerHTML = '';
    document.getElementById('send-location-modal').classList.add('visible');
});

// ▲▲▲ 新代码粘贴结束 ▲▲▲

// ▼▼▼ 在这里粘贴下面这行新代码 ▼▼▼
document.getElementById('reroll-btn').addEventListener('click', handleRerollClick);
// ▲▲▲ 粘贴结束 ▲▲▲

// ▼▼▼ 【全新】粘贴这整块事件监听代码到 init() 末尾 ▼▼▼

// --- 悬浮歌词栏设置功能 ---
document.getElementById('lyrics-settings-btn').addEventListener('click', (e) => {
    e.stopPropagation(); // 阻止事件冒泡触发拖动或打开播放器
    document.getElementById('lyrics-settings-modal').classList.add('visible');
});

document.getElementById('close-lyrics-settings-btn').addEventListener('click', async () => {
    // 保存设置到全局状态并写入数据库
    state.globalSettings.lyricsBarSettings = lyricsBarSettings;
    await db.globalSettings.put(state.globalSettings);
    document.getElementById('lyrics-settings-modal').classList.remove('visible');
    alert('设置已保存！');
});

document.getElementById('reset-lyrics-settings-btn').addEventListener('click', () => {
    // 恢复到默认值
    lyricsBarSettings = { fontSize: 14, bgOpacity: 0, fontColor: '#FFFFFF', showOnClose: true };
    applyLyricsSettings(); // 应用默认设置
});

// 实时更新样式
document.getElementById('lyrics-font-size-slider').addEventListener('input', (e) => {
    lyricsBarSettings.fontSize = e.target.value;
    applyLyricsSettings();
});
document.getElementById('lyrics-bg-opacity-slider').addEventListener('input', (e) => {
    lyricsBarSettings.bgOpacity = e.target.value;
    applyLyricsSettings();
});
document.getElementById('lyrics-font-color-picker').addEventListener('input', (e) => {
    lyricsBarSettings.fontColor = e.target.value;
    applyLyricsSettings();
});

// 歌词栏上的关闭按钮
document.querySelector('#floating-lyrics-bar .close-btn').addEventListener('click', (e) => {
    e.stopPropagation();
    document.getElementById('floating-lyrics-bar').style.display = 'none';
});

// 播放器里的“悬浮/隐藏”开关
document.getElementById('toggle-lyrics-bar-btn').addEventListener('click', async (e) => {
    lyricsBarSettings.showOnClose = !lyricsBarSettings.showOnClose;
    e.target.textContent = lyricsBarSettings.showOnClose ? '悬浮' : '隐藏';
    e.target.style.opacity = lyricsBarSettings.showOnClose ? '1' : '0.5';
    // 立即保存这个设置
    state.globalSettings.lyricsBarSettings = lyricsBarSettings;
    await db.globalSettings.put(state.globalSettings);
});

// 【重要】在页面加载时，就应用一次已保存的设置
applyLyricsSettings();

// ▲▲▲ 新代码粘贴结束 ▲▲▲

            // ▼▼▼ 在 init() 函数的事件监听器区域末尾添加 ▼▼▼
            // 绑定主屏幕滑动事件
            homeScreen.addEventListener('touchstart', handleTouchStart, { passive: true });
            homeScreen.addEventListener('touchmove', handleTouchMove, { passive: true });
            homeScreen.addEventListener('touchend', handleTouchEnd);
            // 兼容桌面端鼠标拖动
            homeScreen.addEventListener('mousedown', handleTouchStart);
            homeScreen.addEventListener('mousemove', handleTouchMove);
            homeScreen.addEventListener('mouseup', handleTouchEnd);
            homeScreen.addEventListener('mouseleave', handleTouchEnd); // 防止鼠标拖出屏幕
            // ▲▲▲ 新代码粘贴结束 ▲▲▲

            // ▼▼▼ 【全新】“查角色手机”功能事件监听器 ▼▼▼

            // 1. 绑定主屏幕上的“查手机”APP图标
            const checkPhoneAppIcon = document.querySelector('.app-icon[data-app-id="check-phone"]');
            if (checkPhoneAppIcon) {
                checkPhoneAppIcon.onclick = openCharacterSelectionScreen; // 修改onclick事件
            }

            // 2. 角色选择列表的点击事件 (事件委托)
            document.getElementById('character-selection-list').addEventListener('click', (e) => {
                const item = e.target.closest('.character-select-item');
                if (item && item.dataset.chatId) {
                    openCharacterPhone(item.dataset.chatId);
                }
            });

            // 【V3版】角色手机聊天列表的点击事件 (事件委托)
            document.getElementById('character-chat-list').addEventListener('click', (e) => {
                const item = e.target.closest('.chat-list-item');
                // 从 data-contact-name 获取要打开的聊天对象的名字
                if (item && item.dataset.contactName) {
                    renderCharacterChatHistory(item.dataset.contactName);
                    showCharacterPhonePage('character-chat-history-screen');
                }
            });

            // 3. 角色手机顶部的“刷新”和“清空”按钮
            document.getElementById('generate-character-data-btn').addEventListener('click', generateCharacterPhoneData);
            document.getElementById('clear-character-data-btn').addEventListener('click', clearCharacterPhoneData);

            // ▲▲▲ 事件监听器添加结束 ▲▲▲

            // ▼▼▼ 【全新】角色手机内部统一返回事件监听器 ▼▼▼
            document.getElementById('character-phone-container').addEventListener('click', (e) => {
                const backBtn = e.target.closest('.back-btn');
                if (!backBtn) return;

                // 检查是返回到角色手机内部页面，还是返回到主屏幕
                if (backBtn.dataset.targetPage) {
                    showCharacterPhonePage(backBtn.dataset.targetPage);
                } else if (backBtn.dataset.targetScreen) {
                    showScreen(backBtn.dataset.targetScreen);
                }
            });
            // ▲▲▲ 新代码粘贴结束 ▲▲▲

            // ▼▼▼ 【全新】角色手机日记APP事件监听器 ▼▼▼
            document.getElementById('character-app-grid').addEventListener('click', (e) => {
                const icon = e.target.closest('.app-icon');
                if (icon && icon.querySelector('.label').textContent === '日记') {
                    renderCharacterDiary();
                }
            });
            document.getElementById('generate-diary-entry-btn').addEventListener('click', generateNewDiaryEntry);
            // ▲▲▲ 新代码粘贴结束 ▲▲▲


            // ===================================================================
            // 5. 启动！
            
            // 应用壁纸并更新所有时钟
            applyLockscreenWallpaper();
            updateLockClock();
            
            // 核心启动逻辑：现在总是显示锁屏界面
            lockPhone();
        }

        init();
    });
</script>
</body>
</html>